home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 7 / Apprentice-Release7.iso / Source Code / C / Applications / Tcl-Tk 8.0 / Pre-installed version / tcl8.0 / mac / tclMacOSA.c < prev    next >
Encoding:
C/C++ Source or Header  |  1997-08-15  |  76.5 KB  |  2,938 lines  |  [TEXT/CWIE]

  1. /* 
  2.  * tclMacOSA.c --
  3.  *
  4.  *    This contains the initialization routines, and the implementation of
  5.  *    the OSA and Component commands.  These commands allow you to connect
  6.  *    with the AppleScript or any other OSA component to compile and execute
  7.  *    scripts.
  8.  *
  9.  * Copyright (c) 1996 Lucent Technologies and Jim Ingham
  10.  * Copyright (c) 1997 Sun Microsystems, Inc.
  11.  *
  12.  * See the file "License Terms" for information on usage and redistribution
  13.  * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
  14.  *
  15.  * SCCS: @(#) tclMacOSA.c 1.7 97/06/18 14:29:58
  16.  */
  17.  
  18. #define MAC_TCL
  19.  
  20. #include <Aliases.h>
  21. #include <string.h>
  22. #include <AppleEvents.h>
  23. #include <AppleScript.h>
  24. #include <OSA.h>
  25. #include <OSAGeneric.h>
  26. #include <Script.h>
  27.  
  28. #include <FullPath.h>
  29. #include <components.h>
  30.  
  31. #include <resources.h>
  32. #include <FSpCompat.h>
  33. /* 
  34.  * The following two Includes are from the More Files package.
  35.  */
  36. #include <MoreFiles.h>
  37. #include <FullPath.h>
  38.  
  39. #include "tcl.h"
  40. #include "tclInt.h"
  41.  
  42. /*
  43.  * I need this only for the call to FspGetFullPath,
  44.  * I'm really not poking my nose where it does not belong!
  45.  */
  46. #include "tclMacInt.h"
  47.  
  48. /*
  49.  * Data structures used by the OSA code.
  50.  */
  51. typedef struct tclOSAScript {
  52.     OSAID scriptID;
  53.     OSType languageID;
  54.     long modeFlags;
  55. } tclOSAScript;
  56.  
  57. typedef struct tclOSAContext {
  58.     OSAID contextID;
  59. } tclOSAContext;
  60.  
  61. typedef struct tclOSAComponent {
  62.     char *theName;
  63.     ComponentInstance theComponent; /* The OSA Component represented */
  64.     long componentFlags;
  65.     OSType languageID;
  66.     char *languageName;
  67.     Tcl_HashTable contextTable;    /* Hash Table linking the context names & ID's */
  68.     Tcl_HashTable scriptTable;
  69.     Tcl_Interp *theInterp;
  70.     OSAActiveUPP defActiveProc;
  71.     long defRefCon;
  72. } tclOSAComponent;
  73.  
  74. /*
  75.  * Prototypes for static procedures. 
  76.  */
  77.  
  78. static pascal OSErr    TclOSAActiveProc _ANSI_ARGS_((long refCon));
  79. static int        TclOSACompileCmd _ANSI_ARGS_((Tcl_Interp *interp,
  80.                  tclOSAComponent *OSAComponent, int argc,
  81.                 char **argv));
  82. static int         tclOSADecompileCmd _ANSI_ARGS_((Tcl_Interp * Interp,
  83.                 tclOSAComponent *OSAComponent, int argc,
  84.                 char **argv));
  85. static int         tclOSADeleteCmd _ANSI_ARGS_((Tcl_Interp *interp,
  86.                 tclOSAComponent *OSAComponent, int argc,
  87.                 char **argv));
  88. static int         tclOSAExecuteCmd _ANSI_ARGS_((Tcl_Interp *interp,
  89.                 tclOSAComponent *OSAComponent, int argc,
  90.                 char **argv));
  91. static int         tclOSAInfoCmd _ANSI_ARGS_((Tcl_Interp *interp,
  92.                 tclOSAComponent *OSAComponent, int argc,
  93.                 char **argv));
  94. static int         tclOSALoadCmd _ANSI_ARGS_((Tcl_Interp *interp,
  95.                 tclOSAComponent *OSAComponent, int argc,
  96.                 char **argv));
  97. static int         tclOSARunCmd _ANSI_ARGS_((Tcl_Interp *interp,
  98.                 tclOSAComponent *OSAComponent, int argc,
  99.                 char **argv));
  100. static int         tclOSAStoreCmd _ANSI_ARGS_((Tcl_Interp *interp,
  101.                 tclOSAComponent *OSAComponent, int argc, char
  102.                 **argv));
  103. static void        GetRawDataFromDescriptor _ANSI_ARGS_((AEDesc *theDesc,
  104.                 Ptr destPtr, Size destMaxSize, Size *actSize));
  105. static OSErr         GetCStringFromDescriptor _ANSI_ARGS_((
  106.                 AEDesc *sourceDesc, char *resultStr,
  107.                 Size resultMaxSize,Size *resultSize));
  108. static int         Tcl_OSAComponentCmd _ANSI_ARGS_((ClientData clientData,
  109.                 Tcl_Interp *interp, int argc, char **argv)); 
  110. static void         getSortedHashKeys _ANSI_ARGS_((Tcl_HashTable *theTable,
  111.                 char *pattern, Tcl_DString *theResult));
  112. static int         ASCIICompareProc _ANSI_ARGS_((const void *first,
  113.                 const void *second));
  114. static int         Tcl_OSACmd _ANSI_ARGS_((ClientData clientData,
  115.                 Tcl_Interp *interp, int argc, char **argv)); 
  116. static void         tclOSAClose _ANSI_ARGS_((ClientData clientData));
  117. static void         tclOSACloseAll _ANSI_ARGS_((ClientData clientData));
  118. static tclOSAComponent *tclOSAMakeNewComponent _ANSI_ARGS_((Tcl_Interp *interp,
  119.                 char *cmdName, char *languageName,
  120.                 OSType scriptSubtype, long componentFlags));  
  121. static int         prepareScriptData _ANSI_ARGS_((int argc, char **argv,
  122.                 Tcl_DString *scrptData ,AEDesc *scrptDesc)); 
  123. static void         tclOSAResultFromID _ANSI_ARGS_((Tcl_Interp *interp,
  124.                 ComponentInstance theComponent, OSAID resultID));
  125. static void         tclOSAASError _ANSI_ARGS_((Tcl_Interp * interp,
  126.                 ComponentInstance theComponent, char *scriptSource));
  127. static int         tclOSAGetContextID _ANSI_ARGS_((tclOSAComponent *theComponent, 
  128.                 char *contextName, OSAID *theContext));
  129. static void         tclOSAAddContext _ANSI_ARGS_((tclOSAComponent *theComponent, 
  130.                 char *contextName, const OSAID theContext));                        
  131. static int         tclOSAMakeContext _ANSI_ARGS_((tclOSAComponent *theComponent, 
  132.                 char *contextName, OSAID *theContext));                        
  133. static int         tclOSADeleteContext _ANSI_ARGS_((tclOSAComponent *theComponent,
  134.                 char *contextName)); 
  135. static int         tclOSALoad _ANSI_ARGS_((Tcl_Interp *interp, 
  136.                 tclOSAComponent *theComponent, char *resourceName, 
  137.                 int resourceNumber, char *fileName,OSAID *resultID));
  138. static int         tclOSAStore _ANSI_ARGS_((Tcl_Interp *interp, 
  139.                 tclOSAComponent *theComponent, char *resourceName, 
  140.                 int resourceNumber, char *fileName,char *scriptName));
  141. static int         tclOSAAddScript _ANSI_ARGS_((tclOSAComponent *theComponent,
  142.                 char *scriptName, long modeFlags, OSAID scriptID));         
  143. static int         tclOSAGetScriptID _ANSI_ARGS_((tclOSAComponent *theComponent,
  144.                 char *scriptName, OSAID *scriptID)); 
  145. static tclOSAScript *    tclOSAGetScript _ANSI_ARGS_((tclOSAComponent *theComponent,
  146.                 char *scriptName)); 
  147. static int         tclOSADeleteScript _ANSI_ARGS_((tclOSAComponent *theComponent,
  148.                 char *scriptName,char *errMsg));
  149.  
  150. /*
  151.  * "export" is a MetroWerks specific pragma.  It flags the linker that  
  152.  * any symbols that are defined when this pragma is on will be exported 
  153.  * to shared libraries that link with this library.
  154.  */
  155.  
  156.  
  157. #pragma export on
  158. int Tclapplescript_Init( Tcl_Interp *interp );
  159. #pragma export reset
  160.  
  161. /*
  162.  *----------------------------------------------------------------------
  163.  *
  164.  * Tclapplescript_Init --
  165.  *
  166.  *    Initializes the the OSA command which opens connections to
  167.  *    OSA components, creates the AppleScript command, which opens an 
  168.  *    instance of the AppleScript component,and constructs the table of
  169.  *    available languages.
  170.  *
  171.  * Results:
  172.  *    A standard Tcl result.
  173.  *
  174.  * Side Effects:
  175.  *    Opens one connection to the AppleScript component, if 
  176.  *    available.  Also builds up a table of available OSA languages,
  177.  *    and creates the OSA command.
  178.  *
  179.  *----------------------------------------------------------------------
  180.  */
  181.  
  182. int 
  183. Tclapplescript_Init(
  184.     Tcl_Interp *interp)        /* Tcl interpreter. */
  185. {
  186.     char *errMsg = NULL;
  187.     OSErr myErr = noErr;
  188.     Boolean gotAppleScript = false;
  189.     Boolean GotOneOSALanguage = false;
  190.     ComponentDescription compDescr = {
  191.     kOSAComponentType,
  192.     (OSType) 0,
  193.     (OSType) 0,
  194.     (long) 0,
  195.     (long) 0
  196.     }, *foundComp;
  197.     Component curComponent = (Component) 0;
  198.     ComponentInstance curOpenComponent;
  199.     Tcl_HashTable *ComponentTable;
  200.     Tcl_HashTable *LanguagesTable;
  201.     Tcl_HashEntry *hashEntry;
  202.     int newPtr;
  203.     AEDesc componentName = { typeNull, NULL };
  204.     char nameStr[32];            
  205.     Size nameLen;
  206.     long appleScriptFlags;
  207.     
  208.     /* 
  209.      * Here We Will Get The Available Osa Languages, Since They Can Only Be 
  210.      * Registered At Startup...  If You Dynamically Load Components, This
  211.      * Will Fail, But This Is Not A Common Thing To Do.
  212.      */
  213.      
  214.     LanguagesTable = (Tcl_HashTable *) ckalloc(sizeof(Tcl_HashTable));
  215.     
  216.     if (LanguagesTable == NULL) {
  217.     panic("Memory Error Allocating Languages Hash Table");
  218.     }
  219.     
  220.     Tcl_SetAssocData(interp, "OSAScript_LangTable", NULL, LanguagesTable);
  221.     Tcl_InitHashTable(LanguagesTable, TCL_STRING_KEYS);
  222.     
  223.             
  224.     while ((curComponent = FindNextComponent(curComponent, &compDescr)) != 0) {
  225.     int nbytes = sizeof(ComponentDescription);
  226.     foundComp = (ComponentDescription *)
  227.         ckalloc(sizeof(ComponentDescription));
  228.     myErr = GetComponentInfo(curComponent, foundComp, NULL, NULL, NULL);
  229.     if (foundComp->componentSubType ==
  230.         kOSAGenericScriptingComponentSubtype) {
  231.         /* Skip the generic component */
  232.         ckfree((char *) foundComp);
  233.     } else {
  234.         GotOneOSALanguage = true;
  235.  
  236.         /*
  237.          * This is gross: looks like I have to open the component just  
  238.          * to get its name!!! GetComponentInfo is supposed to return
  239.          * the name, but AppleScript always returns an empty string.
  240.          */
  241.              
  242.         curOpenComponent = OpenComponent(curComponent);
  243.         if (curOpenComponent == NULL) {
  244.         Tcl_AppendResult(interp,"Error opening component",
  245.             (char *) NULL);
  246.         return TCL_ERROR;
  247.         }
  248.              
  249.         myErr = OSAScriptingComponentName(curOpenComponent,&componentName);
  250.         if (myErr == noErr) {
  251.         myErr = GetCStringFromDescriptor(&componentName,
  252.             nameStr, 31, &nameLen);
  253.         AEDisposeDesc(&componentName);
  254.         }
  255.         CloseComponent(curOpenComponent);
  256.  
  257.         if (myErr == noErr) {
  258.         hashEntry = Tcl_CreateHashEntry(LanguagesTable,
  259.             nameStr, &newPtr);
  260.         Tcl_SetHashValue(hashEntry, (ClientData) foundComp);
  261.         } else {
  262.         Tcl_AppendResult(interp,"Error getting componentName.",
  263.             (char *) NULL);
  264.         return TCL_ERROR;
  265.         }
  266.             
  267.         /*
  268.          * Make sure AppleScript is loaded, otherwise we will
  269.          * not bother to make the AppleScript command.
  270.          */
  271.         if (foundComp->componentSubType == kAppleScriptSubtype) {
  272.         appleScriptFlags = foundComp->componentFlags;
  273.         gotAppleScript = true;
  274.         }            
  275.     }
  276.     }                
  277.  
  278.     /*
  279.      * Create the OSA command.
  280.      */
  281.     
  282.     if (!GotOneOSALanguage) {
  283.     Tcl_AppendResult(interp,"Could not find any OSA languages",
  284.         (char *) NULL);
  285.     return TCL_ERROR;
  286.     }
  287.     
  288.     /*
  289.      * Create the Component Assoc Data & put it in the interpreter.
  290.      */
  291.     
  292.     ComponentTable = (Tcl_HashTable *) ckalloc(sizeof(Tcl_HashTable));
  293.     
  294.     if (ComponentTable == NULL) {
  295.     panic("Memory Error Allocating Hash Table");
  296.     }
  297.     
  298.     Tcl_SetAssocData(interp, "OSAScript_CompTable", NULL, ComponentTable);
  299.             
  300.     Tcl_InitHashTable(ComponentTable, TCL_STRING_KEYS);
  301.  
  302.     /*
  303.      * The OSA command is not currently supported.     
  304.     Tcl_CreateCommand(interp, "OSA", Tcl_OSACmd, (ClientData) NULL,
  305.         (Tcl_CmdDeleteProc *) NULL);
  306.      */
  307.      
  308.     /* 
  309.      * Open up one AppleScript component, with a default context
  310.      * and tie it to the AppleScript command.
  311.      * If the user just wants single-threaded AppleScript execution
  312.      * this should be enough.
  313.      *
  314.      */
  315.      
  316.     if (gotAppleScript) {
  317.     if (tclOSAMakeNewComponent(interp, "AppleScript",
  318.         "AppleScript English", kAppleScriptSubtype,
  319.         appleScriptFlags) == NULL ) {
  320.         return TCL_ERROR;
  321.     }
  322.     }
  323.  
  324.     return Tcl_PkgProvide(interp, "OSAConnect", "1.0");
  325. }
  326.  
  327. /*
  328.  *---------------------------------------------------------------------- 
  329.  *
  330.  * Tcl_OSACmd --
  331.  *
  332.  *    This is the command that provides the interface to the OSA
  333.  *    component manager.  The subcommands are: close: close a component, 
  334.  *    info: get info on components open, and open: get a new connection
  335.  *    with the Scripting Component
  336.  *
  337.  * Results:
  338.  *      A standard Tcl result.
  339.  *
  340.  * Side effects:
  341.  *      Depends on the subcommand, see the user documentation
  342.  *    for more details.
  343.  *
  344.  *----------------------------------------------------------------------
  345.  */
  346.  
  347. int 
  348. Tcl_OSACmd(
  349.     ClientData clientData,
  350.     Tcl_Interp *interp,
  351.     int argc,
  352.     char **argv)
  353. {
  354.     static unsigned short componentCmdIndex = 0;
  355.     char autoName[32];
  356.     char c;
  357.     int length;
  358.     Tcl_HashTable *ComponentTable = NULL;
  359.     
  360.  
  361.     if (argc == 1) {
  362.     Tcl_AppendResult(interp, "Wrong # of arguments, should be \"",
  363.         argv[0], " option\"", (char *) NULL);
  364.     return TCL_ERROR;
  365.     }
  366.     
  367.     c = *argv[1];
  368.     length = strlen(argv[1]);
  369.     
  370.     /*
  371.      * Query out the Component Table, since most of these commands use it...
  372.      */
  373.     
  374.     ComponentTable = (Tcl_HashTable *) Tcl_GetAssocData(interp,
  375.         "OSAScript_CompTable", (Tcl_InterpDeleteProc **) NULL);
  376.     
  377.     if (ComponentTable == NULL) {
  378.     Tcl_AppendResult(interp, "Error, could not get the Component Table",
  379.         " from the Associated data.", (char *) NULL);
  380.     return TCL_ERROR;
  381.     }
  382.     
  383.     if (c == 'c' && strncmp(argv[1],"close",length) == 0) {
  384.     Tcl_HashEntry *hashEntry;
  385.     if (argc != 3) {
  386.         Tcl_AppendResult(interp, "Wrong # of arguments, should be \"",
  387.             argv[0], " ",argv[1], " componentName\"",
  388.             (char *) NULL);
  389.         return TCL_ERROR;
  390.     }
  391.         
  392.     if ((hashEntry = Tcl_FindHashEntry(ComponentTable,argv[2])) == NULL) {
  393.         Tcl_AppendResult(interp, "Component \"", argv[2], "\" not found",
  394.             (char *) NULL);
  395.         return TCL_ERROR;
  396.     } else {
  397.         Tcl_DeleteCommand(interp,argv[2]);
  398.         return TCL_OK;
  399.     }
  400.     } else if (c == 'o' && strncmp(argv[1],"open",length) == 0) {
  401.     /*
  402.      * Default language is AppleScript.
  403.      */
  404.     OSType scriptSubtype = kAppleScriptSubtype;
  405.     char *languageName = "AppleScript English";
  406.     char *errMsg = NULL;
  407.     ComponentDescription *theCD;
  408.  
  409.     argv += 2;
  410.     argc -= 2;
  411.          
  412.     while (argc > 0 ) {
  413.         if (*argv[0] == '-') {
  414.         c = *(argv[0] + 1);
  415.         if (c == 'l' && strcmp(argv[0] + 1, "language") == 0) {
  416.             if (argc == 1) {
  417.             Tcl_AppendResult(interp,
  418.                 "Error - no language provided for the -language switch",
  419.                 (char *) NULL);
  420.             return TCL_ERROR;
  421.             } else {
  422.             Tcl_HashEntry *hashEntry;
  423.             Tcl_HashSearch search;
  424.             Boolean gotIt = false;
  425.             Tcl_HashTable *LanguagesTable;
  426.                         
  427.             /*
  428.              * Look up the language in the languages table
  429.              * Do a simple strstr match, so AppleScript
  430.              * will match "AppleScript English"...
  431.              */
  432.                         
  433.             LanguagesTable = Tcl_GetAssocData(interp,
  434.                 "OSAScript_LangTable",
  435.                 (Tcl_InterpDeleteProc **) NULL);
  436.                             
  437.             for (hashEntry =
  438.                  Tcl_FirstHashEntry(LanguagesTable, &search);
  439.                  hashEntry != NULL;
  440.                  hashEntry = Tcl_NextHashEntry(&search)) {
  441.                 languageName = Tcl_GetHashKey(LanguagesTable,
  442.                     hashEntry);
  443.                 if (strstr(languageName,argv[1]) != NULL) {
  444.                 theCD = (ComponentDescription *)
  445.                     Tcl_GetHashValue(hashEntry);
  446.                 gotIt = true;
  447.                 break;
  448.                 }
  449.             }
  450.             if (!gotIt) {
  451.                 Tcl_AppendResult(interp,
  452.                     "Error, could not find the language \"",
  453.                     argv[1],
  454.                     "\" in the list of known languages.",
  455.                     (char *) NULL);
  456.                 return TCL_ERROR;
  457.             }
  458.             }
  459.         }
  460.         argc -= 2;
  461.         argv += 2;                
  462.         } else {
  463.         Tcl_AppendResult(interp, "Expected a flag, but got ",
  464.             argv[0], (char *) NULL);
  465.         return TCL_ERROR;
  466.         }
  467.     }
  468.             
  469.     sprintf(autoName, "OSAComponent%-d", componentCmdIndex++);
  470.     if (tclOSAMakeNewComponent(interp, autoName, languageName,
  471.         theCD->componentSubType, theCD->componentFlags) == NULL ) {
  472.         return TCL_ERROR;
  473.     } else {
  474.         Tcl_SetResult(interp,autoName,TCL_VOLATILE);
  475.         return TCL_OK;    
  476.     }
  477.         
  478.     } else if (c == 'i' && strncmp(argv[1],"info",length) == 0) {
  479.     if (argc == 2) {
  480.         Tcl_AppendResult(interp, "Wrong # of arguments, should be \"",
  481.             argv[0], " ", argv[1], " what\"",
  482.             (char *) NULL);
  483.         return TCL_ERROR;
  484.     }
  485.              
  486.     c = *argv[2];
  487.     length = strlen(argv[2]);
  488.         
  489.     if (c == 'c' && strncmp(argv[2], "components", length) == 0) {
  490.         Tcl_DString theResult;
  491.             
  492.         Tcl_DStringInit(&theResult);
  493.             
  494.         if (argc == 3) {
  495.         getSortedHashKeys(ComponentTable,(char *) NULL, &theResult);
  496.         } else if (argc == 4) {
  497.         getSortedHashKeys(ComponentTable, argv[3], &theResult);
  498.         } else {
  499.         Tcl_AppendResult(interp, "Error: wrong # of arguments",
  500.             ", should be \"", argv[0], " ", argv[1], " ",
  501.             argv[2], " ?pattern?\".", (char *) NULL);
  502.         return TCL_ERROR;
  503.         }
  504.         Tcl_DStringResult(interp, &theResult);
  505.         return TCL_OK;            
  506.     } else if (c == 'l' && strncmp(argv[2],"languages",length) == 0) {
  507.         Tcl_DString theResult;
  508.         Tcl_HashTable *LanguagesTable;
  509.             
  510.         Tcl_DStringInit(&theResult);
  511.         LanguagesTable = Tcl_GetAssocData(interp,
  512.             "OSAScript_LangTable", (Tcl_InterpDeleteProc **) NULL);
  513.                             
  514.         if (argc == 3) {
  515.         getSortedHashKeys(LanguagesTable, (char *) NULL, &theResult);
  516.         } else if (argc == 4) {
  517.         getSortedHashKeys(LanguagesTable, argv[3], &theResult);
  518.         } else {
  519.         Tcl_AppendResult(interp, "Error: wrong # of arguments",
  520.             ", should be \"", argv[0], " ", argv[1], " ",
  521.             argv[2], " ?pattern?\".", (char *) NULL);
  522.         return TCL_ERROR;
  523.         }
  524.         Tcl_DStringResult(interp,&theResult);
  525.         return TCL_OK;            
  526.     } else {
  527.         Tcl_AppendResult(interp, "Unknown option: ", argv[2],
  528.             " for OSA info, should be one of",
  529.             " \"components\" or \"languages\"",
  530.             (char *) NULL);
  531.         return TCL_ERROR;
  532.     }
  533.     } else {
  534.     Tcl_AppendResult(interp, "Unknown option: ", argv[1],
  535.         ", should be one of \"open\", \"close\" or \"info\".",
  536.         (char *) NULL);
  537.     return TCL_ERROR;
  538.     }
  539.     return TCL_OK;
  540. }
  541.  
  542. /* 
  543.  *----------------------------------------------------------------------
  544.  *
  545.  * Tcl_OSAComponentCmd --
  546.  *
  547.  *    This is the command that provides the interface with an OSA
  548.  *    component.  The sub commands are:
  549.  *    - compile ? -context context? scriptData
  550.  *        compiles the script data, returns the ScriptID
  551.  *    - decompile ? -context context? scriptData
  552.  *        decompiles the script data, source code
  553.  *    - execute ?-context context? scriptData
  554.  *        compiles and runs script data
  555.  *    - info what: get component info
  556.  *    - load ?-flags values? fileName
  557.  *        loads & compiles script data from fileName
  558.  *    - run scriptId ?options?
  559.  *        executes the compiled script 
  560.  *
  561.  * Results:
  562.  *    A standard Tcl result
  563.  *
  564.  * Side Effects:
  565.  *    Depends on the subcommand, see the user documentation
  566.  *    for more details.
  567.  *
  568.  *----------------------------------------------------------------------
  569.  */
  570.  
  571. int 
  572. Tcl_OSAComponentCmd(
  573.     ClientData clientData,
  574.     Tcl_Interp *interp, 
  575.     int argc,
  576.     char **argv)
  577. {
  578.     int length;
  579.     char c;
  580.     
  581.     tclOSAComponent *OSAComponent = (tclOSAComponent *) clientData;
  582.     
  583.     if (argc == 1) {
  584.     Tcl_AppendResult(interp, "wrong # args: should be \"",
  585.         argv[0], " option ?arg ...?\"",
  586.         (char *) NULL);
  587.     return TCL_ERROR;
  588.     }
  589.     
  590.     c = *argv[1];
  591.     length = strlen(argv[1]);
  592.     if (c == 'c' && strncmp(argv[1], "compile", length) == 0) {
  593.     return TclOSACompileCmd(interp, OSAComponent, argc, argv);
  594.     } else if (c == 'l' && strncmp(argv[1], "load", length) == 0) {
  595.     return tclOSALoadCmd(interp, OSAComponent, argc, argv);
  596.     } else if (c == 'e' && strncmp(argv[1], "execute", length) == 0) {
  597.     return tclOSAExecuteCmd(interp, OSAComponent, argc, argv);
  598.     } else if (c == 'i' && strncmp(argv[1], "info", length) == 0) {
  599.     return tclOSAInfoCmd(interp, OSAComponent, argc, argv);
  600.     } else if (c == 'd' && strncmp(argv[1], "decompile", length) == 0) {
  601.     return tclOSADecompileCmd(interp, OSAComponent, argc, argv);
  602.     } else if (c == 'd' && strncmp(argv[1], "delete", length) == 0) {
  603.     return tclOSADeleteCmd(interp, OSAComponent, argc, argv);
  604.     } else if (c == 'r' && strncmp(argv[1], "run", length) == 0) {
  605.     return tclOSARunCmd(interp, OSAComponent, argc, argv);
  606.     } else if (c == 's' && strncmp(argv[1], "store", length) == 0) {
  607.     return tclOSAStoreCmd(interp, OSAComponent, argc, argv);
  608.     } else {
  609.     Tcl_AppendResult(interp,"bad option \"", argv[1],
  610.         "\": should be compile, decompile, delete, ",
  611.          "execute, info, load, run or store",
  612.          (char *) NULL);
  613.     return TCL_ERROR;
  614.     }
  615.  
  616.     return TCL_OK;
  617. }
  618.  
  619. /*
  620.  *----------------------------------------------------------------------
  621.  *
  622.  * TclOSACompileCmd --
  623.  *
  624.  *    This is the compile subcommand for the component command.
  625.  *
  626.  * Results:
  627.  *    A standard Tcl result
  628.  *
  629.  * Side Effects:
  630.  *      Compiles the script data either into a script or a script
  631.  *    context.  Adds the script to the component's script or context
  632.  *    table.  Sets interp's result to the name of the new script or
  633.  *    context.
  634.  *
  635.  *----------------------------------------------------------------------
  636.  */
  637.  
  638. static int 
  639. TclOSACompileCmd(
  640.     Tcl_Interp *interp,
  641.     tclOSAComponent *OSAComponent,
  642.     int argc,
  643.     char **argv)
  644. {
  645.     int  tclError = TCL_OK;
  646.     int augment = 1;
  647.     int makeContext = 0;
  648.     char c;
  649.     char autoName[16];
  650.     char buffer[32];
  651.     char *resultName;
  652.     Boolean makeNewContext = false;
  653.     Tcl_DString scrptData;
  654.     AEDesc scrptDesc = { typeNull, NULL };
  655.     long modeFlags = kOSAModeCanInteract;
  656.     OSAID resultID = kOSANullScript;
  657.     OSAID contextID = kOSANullScript;
  658.     OSAID parentID = kOSANullScript;
  659.     OSAError osaErr = noErr;
  660.     
  661.     if (!(OSAComponent->componentFlags && kOSASupportsCompiling)) {
  662.     Tcl_AppendResult(interp,
  663.         "OSA component does not support compiling",
  664.         (char *) NULL);
  665.     return TCL_ERROR;
  666.     }
  667.  
  668.     /* 
  669.      * This signals that we should make up a name, which is the
  670.      * default behavior:
  671.      */
  672.      
  673.     autoName[0] = '\0';
  674.     resultName = NULL;
  675.     
  676.     if (argc == 2) {
  677.     numArgs:
  678.     Tcl_AppendResult(interp,
  679.         "wrong # args: should be \"", argv[0], " ", argv[1],
  680.         " ?options? code\"",(char *) NULL);
  681.     return TCL_ERROR;
  682.     } 
  683.  
  684.     argv += 2;
  685.     argc -= 2;
  686.  
  687.     /*
  688.      * Do the argument parsing.
  689.      */
  690.     
  691.     while (argc > 0) {
  692.         
  693.     if (*argv[0] == '-') {
  694.         c = *(argv[0] + 1);
  695.             
  696.         /*
  697.          * "--" is the only switch that has no value, stops processing
  698.          */
  699.             
  700.         if (c == '-' && *(argv[0] + 2) == '\0') {
  701.         argv += 1;
  702.         argc--;
  703.         break;
  704.         }
  705.             
  706.         /*
  707.          * So we can check here a switch with no value.
  708.          */
  709.             
  710.         if (argc == 1)  {
  711.         Tcl_AppendResult(interp,
  712.             "no value given for switch: ",
  713.             argv[0], (char *) NULL);
  714.         return TCL_ERROR;
  715.         }
  716.             
  717.         if (c == 'c' && strcmp(argv[0] + 1, "context") == 0) {
  718.         if (Tcl_GetBoolean(interp, argv[1], &makeContext) != TCL_OK) {
  719.             return TCL_ERROR;
  720.         }
  721.         } else if (c == 'a' && strcmp(argv[0] + 1, "augment") == 0) {
  722.         /*
  723.          * Augment the current context which implies making a context.
  724.          */
  725.  
  726.         if (Tcl_GetBoolean(interp, argv[1], &augment) != TCL_OK) {
  727.             return TCL_ERROR;
  728.         }
  729.         makeContext = 1;
  730.         } else if (c == 'n' && strcmp(argv[0] + 1, "name") == 0) {
  731.         resultName = argv[1];
  732.         } else if (c == 'p' && strcmp(argv[0] + 1,"parent") == 0) {
  733.         /*
  734.          * Since this implies we are compiling into a context, 
  735.          * set makeContext here
  736.          */
  737.         if (tclOSAGetContextID(OSAComponent,
  738.             argv[1], &parentID) != TCL_OK) {
  739.             Tcl_AppendResult(interp, "context not found \"",
  740.                 argv[1], "\"", (char *) NULL);
  741.             return TCL_ERROR;
  742.         }
  743.         makeContext = 1;
  744.         } else {
  745.         Tcl_AppendResult(interp, "bad option \"", argv[0],
  746.             "\": should be -augment, -context, -name or -parent",
  747.              (char *) NULL);
  748.         return TCL_ERROR;
  749.         }
  750.         argv += 2;
  751.         argc -= 2;
  752.             
  753.     } else {
  754.         break;
  755.     }
  756.     }
  757.  
  758.     /*
  759.      * Make sure we have some data left...
  760.      */
  761.     if (argc == 0) {
  762.     goto numArgs;
  763.     }
  764.     
  765.     /* 
  766.      * Now if we are making a context, see if it is a new one... 
  767.      * There are three options here:
  768.      * 1) There was no name provided, so we autoName it
  769.      * 2) There was a name, then check and see if it already exists
  770.      *  a) If yes, then makeNewContext is false
  771.      *  b) Otherwise we are making a new context
  772.      */
  773.  
  774.     if (makeContext) {
  775.     modeFlags |= kOSAModeCompileIntoContext;
  776.     if (resultName == NULL) {
  777.         /*
  778.          * Auto name the new context.
  779.          */
  780.         resultName = autoName;
  781.         resultID = kOSANullScript;
  782.         makeNewContext = true;
  783.     } else if (tclOSAGetContextID(OSAComponent,
  784.         resultName, &resultID) == TCL_OK) {
  785.         makeNewContext = false;
  786.     } else { 
  787.         makeNewContext = true;
  788.         resultID = kOSANullScript;
  789.     }
  790.         
  791.     /*
  792.      * Deal with the augment now...
  793.      */
  794.     if (augment && !makeNewContext) {
  795.         modeFlags |= kOSAModeAugmentContext;
  796.     }
  797.     }
  798.     
  799.     /*
  800.      * Ok, now we have the options, so we can compile the script data.
  801.      */
  802.             
  803.     if (prepareScriptData(argc, argv, &scrptData, &scrptDesc) == TCL_ERROR) {
  804.     Tcl_DStringResult(interp, &scrptData);
  805.     AEDisposeDesc(&scrptDesc);
  806.     return TCL_ERROR;
  807.     }
  808.  
  809.     /* 
  810.      * If we want to use a parent context, we have to make the context 
  811.      * by hand. Note, parentID is only specified when you make a new context. 
  812.      */
  813.     
  814.     if (parentID != kOSANullScript && makeNewContext) {
  815.     AEDesc contextDesc = { typeNull, NULL };
  816.  
  817.     osaErr = OSAMakeContext(OSAComponent->theComponent,
  818.         &contextDesc, parentID, &resultID);
  819.     modeFlags |= kOSAModeAugmentContext;
  820.     }
  821.     
  822.     osaErr = OSACompile(OSAComponent->theComponent, &scrptDesc,
  823.         modeFlags, &resultID);                                
  824.     if (osaErr == noErr) {
  825.      
  826.     if (makeContext) {
  827.         /* 
  828.          * For the compiled context to be active, you need to run 
  829.          * the code that is in the context.
  830.          */
  831.         OSAID activateID;
  832.  
  833.         osaErr = OSAExecute(OSAComponent->theComponent, resultID,
  834.             resultID, kOSAModeCanInteract, &activateID);
  835.         OSADispose(OSAComponent->theComponent, activateID);
  836.  
  837.         if (osaErr == noErr) {
  838.         if (makeNewContext) {
  839.             /*
  840.              * If we have compiled into a context, 
  841.              * this is added to the context table 
  842.              */
  843.                      
  844.             tclOSAAddContext(OSAComponent, resultName, resultID);
  845.         }
  846.                 
  847.         Tcl_SetResult(interp, resultName, TCL_VOLATILE);
  848.         tclError = TCL_OK;
  849.         }
  850.     } else {
  851.         /*
  852.          * For a script, we return the script name.
  853.          */
  854.         tclOSAAddScript(OSAComponent, resultName, modeFlags, resultID);
  855.         Tcl_SetResult(interp, resultName, TCL_VOLATILE);
  856.         tclError = TCL_OK;    
  857.     }
  858.     }
  859.     
  860.     /* 
  861.      * This catches the error either from the original compile, 
  862.      * or from the execute in case makeContext == true
  863.      */
  864.                              
  865.     if (osaErr == errOSAScriptError) {
  866.     OSADispose(OSAComponent->theComponent, resultID);
  867.     tclOSAASError(interp, OSAComponent->theComponent,
  868.         Tcl_DStringValue(&scrptData));
  869.     tclError = TCL_ERROR;
  870.     } else if (osaErr != noErr)  {
  871.     sprintf(buffer, "Error #%-6d compiling script", osaErr);
  872.     Tcl_AppendResult(interp, buffer, (char *) NULL);
  873.     tclError = TCL_ERROR;        
  874.     } 
  875.  
  876.     Tcl_DStringFree(&scrptData);
  877.     AEDisposeDesc(&scrptDesc);
  878.     
  879.     return tclError;
  880. }
  881.  
  882. /*
  883.  *----------------------------------------------------------------------
  884.  *
  885.  * tclOSADecompileCmd --
  886.  *
  887.  *     This implements the Decompile subcommand of the component command
  888.  *
  889.  * Results:
  890.  *    A standard Tcl result.
  891.  *
  892.  * Side Effects:
  893.  *      Decompiles the script, and sets interp's result to the
  894.  *    decompiled script data.
  895.  *
  896.  *----------------------------------------------------------------------
  897.  */
  898.          
  899. static int 
  900. tclOSADecompileCmd(
  901.     Tcl_Interp * interp,
  902.     tclOSAComponent *OSAComponent,
  903.     int argc, 
  904.     char **argv)
  905. {
  906.     AEDesc resultingSourceData = { typeChar, NULL };
  907.     OSAID scriptID;
  908.     Boolean isContext;
  909.     long result;
  910.     OSErr sysErr = noErr;
  911.          
  912.     if (argc == 2) {
  913.     Tcl_AppendResult(interp, "Wrong # of arguments, should be \"",
  914.         argv[0], " ",argv[1], " scriptName \"", (char *) NULL );
  915.     return TCL_ERROR;
  916.     }
  917.      
  918.     if (!(OSAComponent->componentFlags && kOSASupportsGetSource)) {
  919.     Tcl_AppendResult(interp,
  920.         "Error, this component does not support get source",
  921.         (char *) NULL);
  922.     return TCL_ERROR;
  923.     }
  924.      
  925.     if (tclOSAGetScriptID(OSAComponent, argv[2], &scriptID) == TCL_OK) {
  926.     isContext = false;
  927.     } else if (tclOSAGetContextID(OSAComponent, argv[2], &scriptID)
  928.         == TCL_OK ) {
  929.     isContext = true;
  930.     } else { 
  931.     Tcl_AppendResult(interp, "Could not find script \"",
  932.         argv[2], "\"", (char *) NULL);
  933.     return TCL_ERROR;
  934.     }
  935.     
  936.     OSAGetScriptInfo(OSAComponent->theComponent, scriptID,
  937.         kOSACanGetSource, &result);
  938.                         
  939.     sysErr = OSAGetSource(OSAComponent->theComponent, 
  940.         scriptID, typeChar, &resultingSourceData);
  941.     
  942.     if (sysErr == noErr) {
  943.     Tcl_DString theResult;
  944.     Tcl_DStringInit(&theResult);
  945.  
  946.     Tcl_DStringAppend(&theResult, *resultingSourceData.dataHandle,
  947.         GetHandleSize(resultingSourceData.dataHandle));
  948.     Tcl_DStringResult(interp, &theResult);
  949.     AEDisposeDesc(&resultingSourceData);
  950.     return TCL_OK;
  951.     } else {
  952.     Tcl_AppendResult(interp, "Error getting source data", (char *) NULL);
  953.     AEDisposeDesc(&resultingSourceData);
  954.     return TCL_ERROR;
  955.     }
  956. }            
  957.          
  958. /*
  959.  *----------------------------------------------------------------------
  960.  *
  961.  * tclOSADeleteCmd --
  962.  *
  963.  *    This implements the Delete subcommand of the Component command.
  964.  *
  965.  * Results:
  966.  *    A standard Tcl result.
  967.  *
  968.  * Side Effects:
  969.  *      Deletes a script from the script list of the given component.
  970.  *    Removes all references to the script, and frees the memory
  971.  *    associated with it.
  972.  *
  973.  *----------------------------------------------------------------------
  974.  */
  975.  
  976. static int 
  977. tclOSADeleteCmd(
  978.     Tcl_Interp *interp,
  979.     tclOSAComponent *OSAComponent,
  980.     int argc,
  981.     char **argv)
  982. {
  983.     char c,*errMsg = NULL;
  984.     int length;
  985.      
  986.     if (argc < 4) {
  987.     Tcl_AppendResult(interp, "Wrong # of arguments, should be \"",
  988.         argv[0], " ", argv[1], " what scriptName", (char *) NULL);
  989.     return TCL_ERROR;
  990.     }
  991.      
  992.     c = *argv[2];
  993.     length = strlen(argv[2]);
  994.     if (c == 'c' && strncmp(argv[2], "context", length) == 0) {
  995.     if (strcmp(argv[3], "global") == 0) {
  996.         Tcl_AppendResult(interp, "You cannot delete the global context",
  997.             (char *) NULL);
  998.         return TCL_ERROR;
  999.     } else if (tclOSADeleteContext(OSAComponent, argv[3]) != TCL_OK) {
  1000.         Tcl_AppendResult(interp, "Error deleting script \"", argv[2],
  1001.             "\": ", errMsg, (char *) NULL);
  1002.         ckfree(errMsg);
  1003.         return TCL_ERROR;
  1004.     }
  1005.     } else if (c == 's' && strncmp(argv[2], "script", length) == 0) {
  1006.     if (tclOSADeleteScript(OSAComponent, argv[3], errMsg) != TCL_OK) {
  1007.         Tcl_AppendResult(interp, "Error deleting script \"", argv[3],
  1008.             "\": ", errMsg, (char *) NULL);
  1009.         ckfree(errMsg);
  1010.         return TCL_ERROR;
  1011.     }
  1012.     } else {
  1013.     Tcl_AppendResult(interp,"Unknown value ", argv[2],
  1014.         " should be one of ",
  1015.         "\"context\" or \"script\".",
  1016.         (char *) NULL );
  1017.     return TCL_ERROR;
  1018.     }
  1019.     return TCL_OK;
  1020. }
  1021.  
  1022. /*
  1023.  *---------------------------------------------------------------------- 
  1024.  *
  1025.  * tclOSAExecuteCmd --
  1026.  *
  1027.  *    This implements the execute subcommand of the component command.
  1028.  *
  1029.  * Results:
  1030.  *    A standard Tcl result.
  1031.  *
  1032.  * Side effects:
  1033.  *    Executes the given script data, and sets interp's result to
  1034.  *    the OSA component's return value.
  1035.  *
  1036.  *---------------------------------------------------------------------- 
  1037.  */
  1038.  
  1039. static int 
  1040. tclOSAExecuteCmd(
  1041.     Tcl_Interp *interp,
  1042.     tclOSAComponent *OSAComponent,
  1043.     int argc,
  1044.     char **argv)
  1045. {
  1046.     int tclError = TCL_OK, resID = 128;
  1047.     char c,buffer[32],
  1048.     *contextName = NULL,*scriptName = NULL, *resName = NULL;
  1049.     Boolean makeNewContext = false,makeContext = false;
  1050.     AEDesc scrptDesc = { typeNull, NULL };
  1051.     long modeFlags = kOSAModeCanInteract;
  1052.     OSAID resultID = kOSANullScript,
  1053.     contextID = kOSANullScript,
  1054.     parentID = kOSANullScript;
  1055.     Tcl_DString scrptData;
  1056.     OSAError osaErr = noErr;
  1057.     OSErr  sysErr = noErr;
  1058.  
  1059.     if (argc == 2) {
  1060.     Tcl_AppendResult(interp,
  1061.         "Error, no script data for \"", argv[0],
  1062.         " run\"", (char *) NULL);
  1063.     return TCL_ERROR;
  1064.     } 
  1065.  
  1066.     argv += 2;
  1067.     argc -= 2;
  1068.  
  1069.     /*
  1070.      * Set the context to the global context by default.
  1071.      * Then parse the argument list for switches
  1072.      */
  1073.     tclOSAGetContextID(OSAComponent, "global", &contextID);
  1074.     
  1075.     while (argc > 0) {
  1076.         
  1077.     if (*argv[0] == '-') {
  1078.         c = *(argv[0] + 1);
  1079.  
  1080.         /*
  1081.          * "--" is the only switch that has no value.
  1082.          */
  1083.             
  1084.         if (c == '-' && *(argv[0] + 2) == '\0') {
  1085.         argv += 1;
  1086.         argc--;
  1087.         break;
  1088.         }
  1089.             
  1090.         /*
  1091.          * So we can check here for a switch with no value.
  1092.          */
  1093.             
  1094.         if (argc == 1)  {
  1095.         Tcl_AppendResult(interp,
  1096.             "Error, no value given for switch ",
  1097.             argv[0], (char *) NULL);
  1098.         return TCL_ERROR;
  1099.         }
  1100.             
  1101.         if (c == 'c' && strcmp(argv[0] + 1, "context") == 0) {
  1102.         if (tclOSAGetContextID(OSAComponent,
  1103.             argv[1], &contextID) == TCL_OK) {
  1104.         } else {
  1105.             Tcl_AppendResult(interp, "Script context \"",
  1106.                 argv[1], "\" not found", (char *) NULL);
  1107.             return TCL_ERROR;
  1108.         }
  1109.         } else { 
  1110.         Tcl_AppendResult(interp, "Error, invalid switch ", argv[0],
  1111.             " should be \"-context\"", (char *) NULL);
  1112.         return TCL_ERROR;
  1113.         }
  1114.             
  1115.         argv += 2;
  1116.         argc -= 2;
  1117.     } else {
  1118.         break;
  1119.     }
  1120.     }
  1121.     
  1122.     if (argc == 0) {
  1123.     Tcl_AppendResult(interp, "Error, no script data", (char *) NULL);
  1124.     return TCL_ERROR;
  1125.     }
  1126.         
  1127.     if (prepareScriptData(argc, argv, &scrptData, &scrptDesc) == TCL_ERROR) {
  1128.     Tcl_DStringResult(interp, &scrptData);
  1129.     AEDisposeDesc(&scrptDesc);
  1130.     return TCL_ERROR;
  1131.     }
  1132.     /*
  1133.      * Now try to compile and run, but check to make sure the
  1134.      * component supports the one shot deal
  1135.      */
  1136.     if (OSAComponent->componentFlags && kOSASupportsConvenience) {
  1137.     osaErr = OSACompileExecute(OSAComponent->theComponent,
  1138.         &scrptDesc, contextID, modeFlags, &resultID);
  1139.     } else {
  1140.     /*
  1141.      * If not, we have to do this ourselves
  1142.      */
  1143.     if (OSAComponent->componentFlags && kOSASupportsCompiling) {
  1144.         OSAID compiledID = kOSANullScript;
  1145.         osaErr = OSACompile(OSAComponent->theComponent, &scrptDesc,
  1146.             modeFlags, &compiledID);
  1147.         if (osaErr == noErr) {
  1148.         osaErr = OSAExecute(OSAComponent->theComponent, compiledID,
  1149.             contextID, modeFlags, &resultID);
  1150.         }
  1151.         OSADispose(OSAComponent->theComponent, compiledID);
  1152.     } else {
  1153.         /*
  1154.          * The scripting component had better be able to load text data...
  1155.          */
  1156.         OSAID loadedID = kOSANullScript;
  1157.             
  1158.         scrptDesc.descriptorType = OSAComponent->languageID;
  1159.         osaErr = OSALoad(OSAComponent->theComponent, &scrptDesc,
  1160.             modeFlags, &loadedID);
  1161.         if (osaErr == noErr) {
  1162.         OSAExecute(OSAComponent->theComponent, loadedID,
  1163.             contextID, modeFlags, &resultID);
  1164.         }
  1165.         OSADispose(OSAComponent->theComponent, loadedID);
  1166.     }
  1167.     }
  1168.     if (osaErr == errOSAScriptError) {
  1169.     tclOSAASError(interp, OSAComponent->theComponent,
  1170.         Tcl_DStringValue(&scrptData));
  1171.     tclError = TCL_ERROR;
  1172.     } else if (osaErr != noErr) {
  1173.     sprintf(buffer, "Error #%-6d compiling script", osaErr);
  1174.     Tcl_AppendResult(interp, buffer, (char *) NULL);
  1175.     tclError = TCL_ERROR;        
  1176.     } else  {
  1177.     tclOSAResultFromID(interp, OSAComponent->theComponent, resultID);
  1178.     osaErr = OSADispose(OSAComponent->theComponent, resultID);
  1179.     tclError = TCL_OK;
  1180.     } 
  1181.  
  1182.     Tcl_DStringFree(&scrptData);
  1183.     AEDisposeDesc(&scrptDesc);    
  1184.  
  1185.     return tclError;    
  1186.  
  1187. /*
  1188.  *----------------------------------------------------------------------
  1189.  *
  1190.  * tclOSAInfoCmd --
  1191.  *
  1192.  * This implements the Info subcommand of the component command
  1193.  *
  1194.  * Results:
  1195.  *    A standard Tcl result.
  1196.  *
  1197.  * Side effects:
  1198.  *    Info on scripts and contexts.  See the user documentation for details.
  1199.  *
  1200.  *----------------------------------------------------------------------
  1201.  */
  1202. static int 
  1203. tclOSAInfoCmd(
  1204.     Tcl_Interp *interp,
  1205.     tclOSAComponent *OSAComponent,
  1206.     int argc, 
  1207.     char **argv)
  1208. {
  1209.     char c;
  1210.     int length;
  1211.     Tcl_DString theResult;
  1212.     
  1213.     if (argc == 2) {
  1214.     Tcl_AppendResult(interp, "Wrong # of arguments, should be \"",
  1215.         argv[0], " ", argv[1], " what \"", (char *) NULL );
  1216.     return TCL_ERROR;
  1217.     }
  1218.      
  1219.     c = *argv[2];
  1220.     length = strlen(argv[2]);
  1221.     if (c == 's' && strncmp(argv[2], "scripts", length) == 0) {
  1222.     Tcl_DStringInit(&theResult);
  1223.     if (argc == 3) {
  1224.         getSortedHashKeys(&OSAComponent->scriptTable, (char *) NULL,
  1225.             &theResult);
  1226.     } else if (argc == 4) {
  1227.         getSortedHashKeys(&OSAComponent->scriptTable, argv[3], &theResult);
  1228.     } else {
  1229.         Tcl_AppendResult(interp, "Error: wrong # of arguments,",
  1230.             " should be \"", argv[0], " ", argv[1], " ",
  1231.             argv[2], " ?pattern?", (char *) NULL);
  1232.         return TCL_ERROR;
  1233.     }
  1234.     Tcl_DStringResult(interp, &theResult);
  1235.     return TCL_OK;            
  1236.     } else if (c == 'c' && strncmp(argv[2], "contexts", length) == 0) {
  1237.     Tcl_DStringInit(&theResult);        
  1238.     if (argc == 3) {
  1239.         getSortedHashKeys(&OSAComponent->contextTable, (char *) NULL,
  1240.            &theResult);
  1241.     } else if (argc == 4) {
  1242.         getSortedHashKeys(&OSAComponent->contextTable,
  1243.             argv[3], &theResult);
  1244.     } else {
  1245.         Tcl_AppendResult(interp, "Error: wrong # of arguments for ,",
  1246.             " should be \"", argv[0], " ", argv[1], " ",
  1247.             argv[2], " ?pattern?", (char *) NULL);
  1248.         return TCL_ERROR;
  1249.     }
  1250.     Tcl_DStringResult(interp, &theResult);
  1251.     return TCL_OK;            
  1252.     } else if (c == 'l' && strncmp(argv[2], "language", length) == 0) {
  1253.     Tcl_SetResult(interp, OSAComponent->languageName, TCL_STATIC);
  1254.     return TCL_OK;
  1255.     } else {
  1256.     Tcl_AppendResult(interp, "Unknown argument \"", argv[2],
  1257.         "\" for \"", argv[0], " info \", should be one of ",
  1258.         "\"scripts\" \"language\", or \"contexts\"",
  1259.         (char *) NULL);
  1260.     return TCL_ERROR;
  1261.     } 
  1262. }
  1263.         
  1264. /*
  1265.  *----------------------------------------------------------------------
  1266.  *
  1267.  * tclOSALoadCmd --
  1268.  *
  1269.  *    This is the load subcommand for the Component Command
  1270.  *
  1271.  *
  1272.  * Results:
  1273.  *    A standard Tcl result.
  1274.  *
  1275.  * Side effects:
  1276.  *    Loads script data from the given file, creates a new context
  1277.  *    for it, and sets interp's result to the name of the new context.
  1278.  *
  1279.  *----------------------------------------------------------------------
  1280.  */
  1281.  
  1282. static int 
  1283. tclOSALoadCmd(
  1284.     Tcl_Interp *interp,
  1285.     tclOSAComponent *OSAComponent,
  1286.     int argc,
  1287.     char **argv)
  1288. {
  1289.     int tclError = TCL_OK, resID = 128;
  1290.     char c, autoName[24],
  1291.     *contextName = NULL, *scriptName = NULL, *resName = NULL;
  1292.     Boolean makeNewContext = false, makeContext = false;
  1293.     AEDesc scrptDesc = { typeNull, NULL };
  1294.     long modeFlags = kOSAModeCanInteract;
  1295.     OSAID resultID = kOSANullScript,
  1296.     contextID = kOSANullScript,
  1297.     parentID = kOSANullScript;
  1298.     OSAError osaErr = noErr;
  1299.     OSErr  sysErr = noErr;
  1300.     long scptInfo;
  1301.     
  1302.     autoName[0] = '\0';
  1303.     scriptName = autoName;
  1304.     contextName = autoName;
  1305.     
  1306.     if (argc == 2) {
  1307.     Tcl_AppendResult(interp,
  1308.         "Error, no data for \"", argv[0], " ", argv[1],
  1309.         "\"", (char *) NULL);
  1310.     return TCL_ERROR;
  1311.     } 
  1312.  
  1313.     argv += 2;
  1314.     argc -= 2;
  1315.  
  1316.     /*
  1317.      * Do the argument parsing.
  1318.      */
  1319.     
  1320.     while (argc > 0) {
  1321.         
  1322.     if (*argv[0] == '-') {
  1323.         c = *(argv[0] + 1);
  1324.             
  1325.         /*
  1326.          * "--" is the only switch that has no value.
  1327.          */
  1328.             
  1329.         if (c == '-' && *(argv[0] + 2) == '\0') {
  1330.         argv += 1;
  1331.         argc--;
  1332.         break;
  1333.         }
  1334.             
  1335.         /*
  1336.          * So we can check here a switch with no value.
  1337.          */
  1338.             
  1339.         if (argc == 1)  {
  1340.         Tcl_AppendResult(interp, "Error, no value given for switch ",
  1341.             argv[0], (char *) NULL);
  1342.         return TCL_ERROR;
  1343.         }
  1344.             
  1345.         if (c == 'r' && strcmp(argv[0] + 1, "rsrcname") == 0) {
  1346.         resName = argv[1];
  1347.         } else if (c == 'r' && strcmp(argv[0] + 1, "rsrcid") == 0) {
  1348.         if (Tcl_GetInt(interp, argv[1], &resID) != TCL_OK) {
  1349.             Tcl_AppendResult(interp,
  1350.                 "Error getting resource ID", (char *) NULL);
  1351.             return TCL_ERROR;
  1352.         }
  1353.         } else {
  1354.         Tcl_AppendResult(interp, "Error, invalid switch ", argv[0],
  1355.             " should be \"--\", \"-rsrcname\" or \"-rsrcid\"",
  1356.             (char *) NULL);
  1357.         return TCL_ERROR;
  1358.         }
  1359.             
  1360.         argv += 2;
  1361.         argc -= 2;
  1362.     } else {
  1363.         break;
  1364.     }
  1365.     }
  1366.     /*
  1367.      * Ok, now we have the options, so we can load the resource,
  1368.      */
  1369.     if (argc == 0) {
  1370.     Tcl_AppendResult(interp, "Error, no filename given", (char *) NULL);
  1371.     return TCL_ERROR;
  1372.     }
  1373.     
  1374.     if (tclOSALoad(interp, OSAComponent, resName, resID,
  1375.         argv[0], &resultID) != TCL_OK) {
  1376.     Tcl_AppendResult(interp, "Error in load command", (char *) NULL);
  1377.     return TCL_ERROR;
  1378.     }
  1379.      
  1380.     /*
  1381.      *  Now find out whether we have a script, or a script context.
  1382.      */
  1383.      
  1384.     OSAGetScriptInfo(OSAComponent->theComponent, resultID,
  1385.         kOSAScriptIsTypeScriptContext, &scptInfo);
  1386.     
  1387.     if (scptInfo) {
  1388.     autoName[0] = '\0';
  1389.     tclOSAAddContext(OSAComponent, autoName, resultID);
  1390.         
  1391.     Tcl_SetResult(interp, autoName, TCL_VOLATILE);
  1392.     } else {
  1393.     /*
  1394.      * For a script, we return the script name
  1395.      */
  1396.     autoName[0] = '\0';
  1397.     tclOSAAddScript(OSAComponent, autoName, kOSAModeCanInteract, resultID);
  1398.     Tcl_SetResult(interp, autoName, TCL_VOLATILE);
  1399.     }             
  1400.     return TCL_OK;
  1401. }
  1402.  
  1403. /*
  1404.  *----------------------------------------------------------------------
  1405.  *
  1406.  * tclOSARunCmd --
  1407.  *
  1408.  *    This implements the run subcommand of the component command
  1409.  *
  1410.  * Results:
  1411.  *    A standard Tcl result.
  1412.  *
  1413.  * Side effects:
  1414.  *    Runs the given compiled script, and returns the OSA
  1415.  *    component's result.
  1416.  *
  1417.  *----------------------------------------------------------------------
  1418.  */
  1419.  
  1420. static int 
  1421. tclOSARunCmd(
  1422.     Tcl_Interp *interp,
  1423.     tclOSAComponent *OSAComponent,
  1424.     int argc,
  1425.     char **argv)
  1426. {
  1427.     int tclError = TCL_OK,
  1428.     resID = 128;
  1429.     char c, *contextName = NULL,
  1430.     *scriptName = NULL, 
  1431.     *resName = NULL;
  1432.     AEDesc scrptDesc = { typeNull, NULL };
  1433.     long modeFlags = kOSAModeCanInteract;
  1434.     OSAID resultID = kOSANullScript,
  1435.     contextID = kOSANullScript,
  1436.     parentID = kOSANullScript;
  1437.     OSAError osaErr = noErr;
  1438.     OSErr sysErr = noErr;
  1439.     char *componentName = argv[0];
  1440.     OSAID scriptID;
  1441.     
  1442.     if (argc == 2) {
  1443.     Tcl_AppendResult(interp, "Wrong # of arguments, should be \"",
  1444.         argv[0], " ", argv[1], " scriptName", (char *) NULL);
  1445.     return TCL_ERROR;
  1446.     }
  1447.     
  1448.     /*
  1449.      * Set the context to the global context for this component,
  1450.      * as a default
  1451.      */
  1452.     if (tclOSAGetContextID(OSAComponent, "global", &contextID) != TCL_OK) {
  1453.     Tcl_AppendResult(interp,
  1454.         "Could not find the global context for component ",
  1455.         OSAComponent->theName, (char *) NULL );
  1456.     return TCL_ERROR;
  1457.     }
  1458.  
  1459.     /*
  1460.      * Now parse the argument list for switches
  1461.      */
  1462.     argv += 2;
  1463.     argc -= 2;
  1464.     
  1465.     while (argc > 0) {
  1466.     if (*argv[0] == '-') {
  1467.         c = *(argv[0] + 1);
  1468.         /*
  1469.          * "--" is the only switch that has no value
  1470.          */
  1471.         if (c == '-' && *(argv[0] + 2) == '\0') {
  1472.         argv += 1;
  1473.         argc--;
  1474.         break;
  1475.         }
  1476.             
  1477.         /*
  1478.          * So we can check here for a switch with no value.
  1479.          */
  1480.         if (argc == 1)  {
  1481.         Tcl_AppendResult(interp, "Error, no value given for switch ",
  1482.             argv[0], (char *) NULL);
  1483.         return TCL_ERROR;
  1484.         }
  1485.             
  1486.         if (c == 'c' && strcmp(argv[0] + 1, "context") == 0) {
  1487.         if (argc == 1) {
  1488.             Tcl_AppendResult(interp,
  1489.                 "Error - no context provided for the -context switch",
  1490.                 (char *) NULL);
  1491.             return TCL_ERROR;
  1492.         } else if (tclOSAGetContextID(OSAComponent,
  1493.             argv[1], &contextID) == TCL_OK) {
  1494.         } else {
  1495.             Tcl_AppendResult(interp, "Script context \"", argv[1],
  1496.                 "\" not found", (char *) NULL);
  1497.             return TCL_ERROR;
  1498.         } 
  1499.         } else {
  1500.         Tcl_AppendResult(interp, "Error, invalid switch ", argv[0],
  1501.             " for ", componentName,
  1502.             " should be \"-context\"", (char *) NULL);
  1503.         return TCL_ERROR;
  1504.         }
  1505.         argv += 2;
  1506.         argc -= 2;
  1507.     } else {
  1508.         break;
  1509.     }
  1510.     }
  1511.     
  1512.     if (tclOSAGetScriptID(OSAComponent, argv[0], &scriptID) != TCL_OK) {
  1513.     if (tclOSAGetContextID(OSAComponent, argv[0], &scriptID) != TCL_OK) {
  1514.         Tcl_AppendResult(interp, "Could not find script \"",
  1515.             argv[2], "\"", (char *) NULL);
  1516.         return TCL_ERROR;
  1517.     }
  1518.     }
  1519.     
  1520.     sysErr = OSAExecute(OSAComponent->theComponent,
  1521.         scriptID, contextID, modeFlags, &resultID);
  1522.                             
  1523.     if (sysErr == errOSAScriptError) {
  1524.     tclOSAASError(interp, OSAComponent->theComponent, (char *) NULL);
  1525.     tclError = TCL_ERROR;
  1526.     } else if (sysErr != noErr) {
  1527.     char buffer[32];
  1528.     sprintf(buffer, "Error #%6.6d encountered in run", sysErr);
  1529.     Tcl_SetResult(interp, buffer, TCL_VOLATILE);
  1530.     tclError = TCL_ERROR;
  1531.     } else {
  1532.     tclOSAResultFromID(interp, OSAComponent->theComponent, resultID );
  1533.     }
  1534.     OSADispose(OSAComponent->theComponent, resultID);
  1535.  
  1536.     return tclError;        
  1537. }
  1538.  
  1539. /*
  1540.  *----------------------------------------------------------------------
  1541.  *
  1542.  * tclOSAStoreCmd --
  1543.  *
  1544.  *    This implements the store subcommand of the component command
  1545.  *
  1546.  * Results:
  1547.  *    A standard Tcl result.
  1548.  *
  1549.  * Side effects:
  1550.  *    Runs the given compiled script, and returns the OSA
  1551.  *    component's result.
  1552.  *
  1553.  *----------------------------------------------------------------------
  1554.  */
  1555.  
  1556. static int 
  1557. tclOSAStoreCmd(
  1558.     Tcl_Interp *interp,
  1559.     tclOSAComponent *OSAComponent,
  1560.     int argc,
  1561.     char **argv)
  1562. {
  1563.     int tclError = TCL_OK, resID = 128;
  1564.     char c, *contextName = NULL, *scriptName = NULL, *resName = NULL;
  1565.     Boolean makeNewContext = false, makeContext = false;
  1566.     AEDesc scrptDesc = { typeNull, NULL };
  1567.     long modeFlags = kOSAModeCanInteract;
  1568.     OSAID resultID = kOSANullScript,
  1569.     contextID = kOSANullScript,
  1570.     parentID = kOSANullScript;
  1571.     OSAError osaErr = noErr;
  1572.     OSErr  sysErr = noErr;
  1573.         
  1574.     if (argc == 2) {
  1575.     Tcl_AppendResult(interp, "Error, no data for \"", argv[0],
  1576.         " ",argv[1], "\"", (char *) NULL);
  1577.     return TCL_ERROR;
  1578.     } 
  1579.  
  1580.     argv += 2;
  1581.     argc -= 2;
  1582.  
  1583.     /*
  1584.      * Do the argument parsing
  1585.      */
  1586.     
  1587.     while (argc > 0) {
  1588.     if (*argv[0] == '-') {
  1589.         c = *(argv[0] + 1);
  1590.             
  1591.         /*
  1592.          * "--" is the only switch that has no value
  1593.          */
  1594.         if (c == '-' && *(argv[0] + 2) == '\0') {
  1595.         argv += 1;
  1596.         argc--;
  1597.         break;
  1598.         }
  1599.             
  1600.         /*
  1601.          * So we can check here a switch with no value.
  1602.          */
  1603.         if (argc == 1)  {
  1604.         Tcl_AppendResult(interp,
  1605.             "Error, no value given for switch ",
  1606.             argv[0], (char *) NULL);
  1607.         return TCL_ERROR;
  1608.         }
  1609.             
  1610.         if (c == 'r' && strcmp(argv[0] + 1, "rsrcname") == 0) {
  1611.         resName = argv[1];
  1612.         } else if (c == 'r' && strcmp(argv[0] + 1, "rsrcid") == 0) {
  1613.         if (Tcl_GetInt(interp, argv[1], &resID) != TCL_OK) {
  1614.             Tcl_AppendResult(interp,
  1615.                 "Error getting resource ID", (char *) NULL);
  1616.             return TCL_ERROR;
  1617.         }
  1618.         } else {
  1619.         Tcl_AppendResult(interp, "Error, invalid switch ", argv[0],
  1620.             " should be \"--\", \"-rsrcname\" or \"-rsrcid\"",
  1621.             (char *) NULL);
  1622.         return TCL_ERROR;
  1623.         }
  1624.             
  1625.         argv += 2;
  1626.         argc -= 2;
  1627.     } else {
  1628.         break;
  1629.     }
  1630.     }
  1631.     /*
  1632.      * Ok, now we have the options, so we can load the resource,
  1633.      */
  1634.     if (argc != 2) {
  1635.     Tcl_AppendResult(interp, "Error, wrong # of arguments, should be ",
  1636.         argv[0], " ", argv[1], "?option flag? scriptName fileName",
  1637.         (char *) NULL);
  1638.     return TCL_ERROR;
  1639.     }
  1640.     
  1641.     if (tclOSAStore(interp, OSAComponent, resName, resID,
  1642.         argv[0], argv[1]) != TCL_OK) {
  1643.     Tcl_AppendResult(interp, "Error in load command", (char *) NULL);
  1644.     return TCL_ERROR;
  1645.     } else {
  1646.     Tcl_ResetResult(interp);
  1647.     tclError = TCL_OK;
  1648.     }
  1649.     
  1650.     return tclError;
  1651. }
  1652.  
  1653. /*
  1654.  *----------------------------------------------------------------------
  1655.  *
  1656.  * tclOSAMakeNewComponent --
  1657.  *
  1658.  *    Makes a command cmdName to represent a new connection to the
  1659.  *    OSA component with componentSubType scriptSubtype.
  1660.  *
  1661.  * Results: 
  1662.  *    Returns the tclOSAComponent structure for the connection.
  1663.  *
  1664.  * Side Effects: 
  1665.  *    Adds a new element to the component table.  If there is an
  1666.  *    error, then the result of the Tcl interpreter interp is set
  1667.  *    to an appropriate error message.
  1668.  *
  1669.  *----------------------------------------------------------------------
  1670.  */
  1671.  
  1672. tclOSAComponent *
  1673. tclOSAMakeNewComponent(
  1674.     Tcl_Interp *interp,
  1675.     char *cmdName,
  1676.     char *languageName, 
  1677.     OSType scriptSubtype,
  1678.     long componentFlags) 
  1679. {
  1680.     char buffer[32];
  1681.     AEDesc resultingName = {typeNull, NULL};
  1682.     AEDesc nullDesc = {typeNull, NULL };
  1683.     OSAID globalContext;
  1684.     char global[] = "global";
  1685.     int nbytes;
  1686.     ComponentDescription requestedComponent = {
  1687.     kOSAComponentType,
  1688.     (OSType) 0,
  1689.     (OSType) 0,
  1690.     (long int) 0,
  1691.     (long int) 0
  1692.     };
  1693.     Tcl_HashTable *ComponentTable;
  1694.     Component foundComponent = NULL;
  1695.     OSAActiveUPP myActiveProcUPP;
  1696.             
  1697.     tclOSAComponent *newComponent;
  1698.     Tcl_HashEntry *hashEntry;
  1699.     int newPtr;
  1700.     
  1701.     requestedComponent.componentSubType = scriptSubtype;
  1702.     nbytes = sizeof(tclOSAComponent);
  1703.     newComponent = (tclOSAComponent *) ckalloc(sizeof(tclOSAComponent));
  1704.     if (newComponent == NULL) {
  1705.     goto CleanUp;
  1706.     }
  1707.     
  1708.     foundComponent = FindNextComponent(0, &requestedComponent);
  1709.     if (foundComponent == 0) {
  1710.     Tcl_AppendResult(interp,
  1711.         "Could not find component of requested type", (char *) NULL);
  1712.     goto CleanUp;
  1713.     } 
  1714.     
  1715.     newComponent->theComponent = OpenComponent(foundComponent); 
  1716.     
  1717.     if (newComponent->theComponent == NULL) {
  1718.     Tcl_AppendResult(interp,
  1719.         "Could not open component of the requested type",
  1720.         (char *) NULL);
  1721.     goto CleanUp;
  1722.     }
  1723.                             
  1724.     newComponent->languageName = (char *) ckalloc(strlen(languageName) + 1);
  1725.     strcpy(newComponent->languageName,languageName);
  1726.     
  1727.     newComponent->componentFlags = componentFlags;
  1728.     
  1729.     newComponent->theInterp = interp;
  1730.     
  1731.     Tcl_InitHashTable(&newComponent->contextTable, TCL_STRING_KEYS);
  1732.     Tcl_InitHashTable(&newComponent->scriptTable, TCL_STRING_KEYS);
  1733.         
  1734.     if (tclOSAMakeContext(newComponent, global, &globalContext) != TCL_OK) {
  1735.     sprintf(buffer, "%-6.6d", globalContext);
  1736.     Tcl_AppendResult(interp, "Error ", buffer, " making ", global,
  1737.         " context.", (char *) NULL);
  1738.     goto CleanUp;
  1739.     }
  1740.     
  1741.     newComponent->languageID = scriptSubtype;
  1742.     
  1743.     newComponent->theName = (char *) ckalloc(strlen(cmdName) + 1 );
  1744.     strcpy(newComponent->theName, cmdName);
  1745.  
  1746.     Tcl_CreateCommand(interp, newComponent->theName, Tcl_OSAComponentCmd,
  1747.         (ClientData) newComponent, tclOSAClose);
  1748.                     
  1749.     /*
  1750.      * Register the new component with the component table
  1751.      */ 
  1752.  
  1753.     ComponentTable = (Tcl_HashTable *) Tcl_GetAssocData(interp,
  1754.         "OSAScript_CompTable", (Tcl_InterpDeleteProc **) NULL);
  1755.     
  1756.     if (ComponentTable == NULL) {
  1757.     Tcl_AppendResult(interp, "Error, could not get the Component Table",
  1758.         " from the Associated data.", (char *) NULL);
  1759.     return (tclOSAComponent *) NULL;
  1760.     }
  1761.     
  1762.     hashEntry = Tcl_CreateHashEntry(ComponentTable,
  1763.         newComponent->theName, &newPtr);    
  1764.     Tcl_SetHashValue(hashEntry, (ClientData) newComponent);
  1765.  
  1766.     /*
  1767.      * Set the active proc to call Tcl_DoOneEvent() while idle
  1768.      */
  1769.     if (OSAGetActiveProc(newComponent->theComponent,
  1770.         &newComponent->defActiveProc, &newComponent->defRefCon) != noErr ) {
  1771.         /* TODO -- clean up here... */
  1772.     }
  1773.  
  1774.     myActiveProcUPP = NewOSAActiveProc(TclOSAActiveProc);
  1775.     OSASetActiveProc(newComponent->theComponent,
  1776.         myActiveProcUPP, (long) newComponent);
  1777.     return newComponent;
  1778.     
  1779.     CleanUp:
  1780.     
  1781.     ckfree((char *) newComponent);
  1782.     return (tclOSAComponent *) NULL;
  1783. }
  1784.  
  1785. /*
  1786.  *----------------------------------------------------------------------
  1787.  *
  1788.  * tclOSAClose --
  1789.  *
  1790.  *    This procedure closes the connection to an OSA component, and 
  1791.  *    deletes all the script and context data associated with it.
  1792.  *    It is the command deletion callback for the component's command.
  1793.  *
  1794.  * Results:
  1795.  *    None
  1796.  *
  1797.  * Side effects:
  1798.  *    Closes the connection, and releases all the script data.
  1799.  *
  1800.  *----------------------------------------------------------------------
  1801.  */
  1802.  
  1803. void 
  1804. tclOSAClose(
  1805.     ClientData clientData) 
  1806. {
  1807.     tclOSAComponent *theComponent = (tclOSAComponent *) clientData;
  1808.     Tcl_HashEntry *hashEntry;
  1809.     Tcl_HashSearch search;
  1810.     tclOSAScript *theScript;
  1811.     Tcl_HashTable *ComponentTable;
  1812.     
  1813.     /* 
  1814.      * Delete the context and script tables 
  1815.      * the memory for the language name, and
  1816.      * the hash entry.
  1817.      */
  1818.     
  1819.     for (hashEntry = Tcl_FirstHashEntry(&theComponent->scriptTable, &search);
  1820.      hashEntry != NULL;
  1821.      hashEntry = Tcl_NextHashEntry(&search)) {
  1822.  
  1823.     theScript = (tclOSAScript *) Tcl_GetHashValue(hashEntry);
  1824.     OSADispose(theComponent->theComponent, theScript->scriptID);    
  1825.     ckfree((char *) theScript);
  1826.     Tcl_DeleteHashEntry(hashEntry);
  1827.     }
  1828.     
  1829.     for (hashEntry = Tcl_FirstHashEntry(&theComponent->contextTable, &search);
  1830.      hashEntry != NULL;
  1831.      hashEntry = Tcl_NextHashEntry(&search)) {
  1832.  
  1833.     Tcl_DeleteHashEntry(hashEntry);
  1834.     }
  1835.     
  1836.     ckfree(theComponent->languageName);
  1837.     ckfree(theComponent->theName);
  1838.     
  1839.     /*
  1840.      * Finally close the component
  1841.      */
  1842.     
  1843.     CloseComponent(theComponent->theComponent);
  1844.     
  1845.     ComponentTable = (Tcl_HashTable *)
  1846.     Tcl_GetAssocData(theComponent->theInterp,
  1847.         "OSAScript_CompTable", (Tcl_InterpDeleteProc **) NULL);
  1848.     
  1849.     if (ComponentTable == NULL) {
  1850.     panic("Error, could not get the Component Table from the Associated data.");
  1851.     }
  1852.     
  1853.     hashEntry = Tcl_FindHashEntry(ComponentTable, theComponent->theName);
  1854.     if (hashEntry != NULL) {
  1855.     Tcl_DeleteHashEntry(hashEntry);
  1856.     }
  1857.     
  1858.     ckfree((char *) theComponent);
  1859. }
  1860.  
  1861. /*
  1862.  *----------------------------------------------------------------------
  1863.  *
  1864.  * tclOSAGetContextID  --
  1865.  *
  1866.  *    This returns the context ID, given the component name.
  1867.  *
  1868.  * Results:
  1869.  *    A context ID
  1870.  *
  1871.  * Side effects:
  1872.  *    None
  1873.  *
  1874.  *----------------------------------------------------------------------
  1875.  */
  1876.  
  1877. static int 
  1878. tclOSAGetContextID(
  1879.     tclOSAComponent *theComponent, 
  1880.     char *contextName, 
  1881.     OSAID *theContext)
  1882. {
  1883.     Tcl_HashEntry *hashEntry;
  1884.     tclOSAContext *contextStruct;
  1885.     
  1886.     if ((hashEntry = Tcl_FindHashEntry(&theComponent->contextTable,
  1887.         contextName)) == NULL ) {            
  1888.     return TCL_ERROR;
  1889.     } else {
  1890.     contextStruct = (tclOSAContext *) Tcl_GetHashValue(hashEntry);
  1891.     *theContext = contextStruct->contextID;
  1892.     }
  1893.     return TCL_OK;
  1894. }
  1895.  
  1896. /*
  1897.  *----------------------------------------------------------------------
  1898.  *
  1899.  * tclOSAAddContext  --
  1900.  *
  1901.  *    This adds the context ID, with the name contextName.  If the
  1902.  *    name is passed in as a NULL string, space is malloc'ed for the
  1903.  *    string and a new name is made up, if the string is empty, you
  1904.  *    must have allocated enough space ( 24 characters is fine) for
  1905.  *    the name, which is made up and passed out.
  1906.  *
  1907.  * Results:
  1908.  *    Nothing
  1909.  *
  1910.  * Side effects:
  1911.  *    Adds the script context to the component's context table.
  1912.  *
  1913.  *----------------------------------------------------------------------
  1914.  */
  1915.  
  1916. static void 
  1917. tclOSAAddContext(
  1918.     tclOSAComponent *theComponent, 
  1919.     char *contextName,
  1920.     const OSAID theContext)
  1921. {
  1922.     static unsigned short contextIndex = 0;
  1923.     tclOSAContext *contextStruct;
  1924.     Tcl_HashEntry *hashEntry;
  1925.     int newPtr;
  1926.  
  1927.     if (contextName == NULL) {
  1928.     contextName = ckalloc(24 * sizeof(char));
  1929.     sprintf(contextName, "OSAContext%d", contextIndex++);
  1930.     } else if (*contextName == '\0') {
  1931.     sprintf(contextName, "OSAContext%d", contextIndex++);
  1932.     }
  1933.     
  1934.     hashEntry = Tcl_CreateHashEntry(&theComponent->contextTable,
  1935.         contextName, &newPtr);    
  1936.  
  1937.     contextStruct = (tclOSAContext *) ckalloc(sizeof(tclOSAContext));
  1938.     contextStruct->contextID = theContext;
  1939.     Tcl_SetHashValue(hashEntry,(ClientData) contextStruct);
  1940. }
  1941.  
  1942. /*
  1943.  *----------------------------------------------------------------------
  1944.  *
  1945.  * tclOSADeleteContext  --
  1946.  *
  1947.  *    This deletes the context struct, with the name contextName.  
  1948.  *
  1949.  * Results:
  1950.  *    A normal Tcl result
  1951.  *
  1952.  * Side effects:
  1953.  *    Removes the script context to the component's context table,
  1954.  *    and deletes the data associated with it.
  1955.  *
  1956.  *----------------------------------------------------------------------
  1957.  */
  1958.  
  1959. static int 
  1960. tclOSADeleteContext(
  1961.     tclOSAComponent *theComponent,
  1962.     char *contextName) 
  1963. {
  1964.     Tcl_HashEntry *hashEntry;
  1965.     tclOSAContext *contextStruct;
  1966.     
  1967.     hashEntry = Tcl_FindHashEntry(&theComponent->contextTable, contextName);
  1968.     if (hashEntry == NULL) {
  1969.     return TCL_ERROR;
  1970.     }    
  1971.     /*
  1972.      * Dispose of the script context data
  1973.      */
  1974.     contextStruct = (tclOSAContext *) Tcl_GetHashValue(hashEntry);
  1975.     OSADispose(theComponent->theComponent,contextStruct->contextID);
  1976.     /*
  1977.      * Then the hash entry
  1978.      */
  1979.     ckfree((char *) contextStruct);
  1980.     Tcl_DeleteHashEntry(hashEntry);
  1981.     return TCL_OK;
  1982. }
  1983.  
  1984. /*
  1985.  *----------------------------------------------------------------------
  1986.  *
  1987.  * tclOSAMakeContext  --
  1988.  *
  1989.  *    This makes the context with name contextName, and returns the ID.
  1990.  *
  1991.  * Results:
  1992.  *    A standard Tcl result
  1993.  *
  1994.  * Side effects:
  1995.  *    Makes a new context, adds it to the context table, and returns 
  1996.  *    the new contextID in the variable theContext.
  1997.  *
  1998.  *----------------------------------------------------------------------
  1999.  */
  2000.  
  2001. static int 
  2002. tclOSAMakeContext(
  2003.     tclOSAComponent *theComponent, 
  2004.     char *contextName,
  2005.     OSAID *theContext)
  2006. {
  2007.     AEDesc contextNameDesc = {typeNull, NULL};
  2008.     OSAError osaErr = noErr;
  2009.  
  2010.     AECreateDesc(typeChar, contextName, strlen(contextName), &contextNameDesc);
  2011.     osaErr = OSAMakeContext(theComponent->theComponent, &contextNameDesc,
  2012.         kOSANullScript, theContext);
  2013.                                 
  2014.     AEDisposeDesc(&contextNameDesc);
  2015.     
  2016.     if (osaErr == noErr) {
  2017.     tclOSAAddContext(theComponent, contextName, *theContext);
  2018.     } else {
  2019.     *theContext = (OSAID) osaErr;
  2020.     return TCL_ERROR;
  2021.     }
  2022.     
  2023.     return TCL_OK;
  2024. }
  2025.  
  2026. /*
  2027.  *----------------------------------------------------------------------
  2028.  *
  2029.  * tclOSAStore --
  2030.  *
  2031.  *    This stores a script resource from the file named in fileName.
  2032.  *
  2033.  *    Most of this routine is caged from the Tcl Source, from the
  2034.  *    Tcl_MacSourceCmd routine.  This is good, since it ensures this
  2035.  *    follows the same convention for looking up files as Tcl.
  2036.  *
  2037.  * Returns
  2038.  *    A standard Tcl result.
  2039.  *
  2040.  * Side Effects:
  2041.  *    The given script data is stored in the file fileName.
  2042.  *
  2043.  *----------------------------------------------------------------------
  2044.  */
  2045.  
  2046. int
  2047. tclOSAStore(
  2048.     Tcl_Interp *interp,
  2049.     tclOSAComponent *theComponent,
  2050.     char *resourceName,
  2051.     int resourceNumber, 
  2052.     char *scriptName,
  2053.     char *fileName)
  2054. {
  2055.     Handle resHandle;
  2056.     Str255 rezName;
  2057.     int result = TCL_OK;
  2058.     short saveRef, fileRef = -1;
  2059.     char idStr[64];
  2060.     FSSpec fileSpec;
  2061.     Tcl_DString buffer;
  2062.     char *nativeName;
  2063.     OSErr myErr = noErr;
  2064.     OSAID scriptID;
  2065.     Size scriptSize;
  2066.     AEDesc scriptData;
  2067.  
  2068.     /*
  2069.      * First extract the script data
  2070.      */
  2071.     
  2072.     if (tclOSAGetScriptID(theComponent, scriptName, &scriptID) != TCL_OK ) {
  2073.     if (tclOSAGetContextID(theComponent, scriptName, &scriptID)
  2074.         != TCL_OK) {
  2075.         Tcl_AppendResult(interp, "Error getting script ",
  2076.             scriptName, (char *) NULL);
  2077.         return TCL_ERROR;
  2078.     }
  2079.     }
  2080.     
  2081.     myErr = OSAStore(theComponent->theComponent, scriptID,
  2082.         typeOSAGenericStorage, kOSAModeNull, &scriptData);
  2083.     if (myErr != noErr) {
  2084.     sprintf(idStr, "%d", myErr);
  2085.     Tcl_AppendResult(interp, "Error #", idStr,
  2086.         " storing script ", scriptName, (char *) NULL);
  2087.     return TCL_ERROR;
  2088.     }
  2089.  
  2090.     /*
  2091.      * Now try to open the output file
  2092.      */
  2093.     
  2094.     saveRef = CurResFile();
  2095.     
  2096.     if (fileName != NULL) {
  2097.     OSErr err;
  2098.         
  2099.     Tcl_DStringInit(&buffer);    
  2100.     nativeName = Tcl_TranslateFileName(interp, fileName, &buffer);
  2101.     if (nativeName == NULL) {
  2102.         return TCL_ERROR;
  2103.     }
  2104.     err = FSpLocationFromPath(strlen(nativeName), nativeName, &fileSpec);
  2105.         
  2106.     Tcl_DStringFree(&buffer);
  2107.     if ((err != noErr) && (err != fnfErr)) {
  2108.         Tcl_AppendResult(interp,
  2109.             "Error getting a location for the file: \"", 
  2110.             fileName, "\".", NULL);
  2111.         return TCL_ERROR;
  2112.     }
  2113.         
  2114.     FSpCreateResFileCompat(&fileSpec,
  2115.         'WiSH', 'osas', smSystemScript);    
  2116.     myErr = ResError();
  2117.     
  2118.     if ((myErr != noErr) && (myErr != dupFNErr)) {
  2119.         sprintf(idStr, "%d", myErr);
  2120.         Tcl_AppendResult(interp, "Error #", idStr,
  2121.             " creating new resource file ", fileName, (char *) NULL);
  2122.         result = TCL_ERROR;
  2123.         goto rezEvalCleanUp;
  2124.     }
  2125.         
  2126.     fileRef = FSpOpenResFileCompat(&fileSpec, fsRdWrPerm);
  2127.     if (fileRef == -1) {
  2128.         Tcl_AppendResult(interp, "Error reading the file: \"", 
  2129.             fileName, "\".", NULL);
  2130.         result = TCL_ERROR;
  2131.         goto rezEvalCleanUp;
  2132.     }
  2133.     UseResFile(fileRef);
  2134.     } else {
  2135.     /*
  2136.      * The default behavior will search through all open resource files.
  2137.      * This may not be the behavior you desire.  If you want the behavior
  2138.      * of this call to *only* search the application resource fork, you
  2139.      * must call UseResFile at this point to set it to the application
  2140.      * file.  This means you must have already obtained the application's 
  2141.      * fileRef when the application started up.
  2142.      */
  2143.     }
  2144.     
  2145.     /*
  2146.      * Load the resource by name 
  2147.      */
  2148.     if (resourceName != NULL) {
  2149.     strcpy((char *) rezName + 1, resourceName);
  2150.     rezName[0] = strlen(resourceName);
  2151.     resHandle = Get1NamedResource('scpt', rezName);
  2152.     myErr = ResError();
  2153.     if (resHandle == NULL) {
  2154.         /*
  2155.          * These signify either the resource or the resource
  2156.          * type were not found
  2157.          */
  2158.         if (myErr == resNotFound || myErr == noErr) {
  2159.         short uniqueID;
  2160.         while ((uniqueID = Unique1ID('scpt') ) < 128) {}
  2161.         AddResource(scriptData.dataHandle, 'scpt', uniqueID, rezName);
  2162.         WriteResource(resHandle);
  2163.         result = TCL_OK;
  2164.         goto rezEvalCleanUp;
  2165.         } else {
  2166.         /*
  2167.          * This means there was some other error, for now
  2168.          * I just bag out.
  2169.          */
  2170.         sprintf(idStr, "%d", myErr);
  2171.         Tcl_AppendResult(interp, "Error #", idStr,
  2172.             " opening scpt resource named ", resourceName,
  2173.             " in file ", fileName, (char *) NULL);
  2174.         result = TCL_ERROR;
  2175.         goto rezEvalCleanUp;
  2176.         }
  2177.     }
  2178.     /*
  2179.      * Or ID
  2180.      */ 
  2181.     } else {
  2182.     resHandle = Get1Resource('scpt', resourceNumber);
  2183.     rezName[0] = 0;
  2184.     rezName[1] = '\0';
  2185.     myErr = ResError();
  2186.     if (resHandle == NULL) {
  2187.         /*
  2188.          * These signify either the resource or the resource
  2189.          * type were not found
  2190.          */
  2191.         if (myErr == resNotFound || myErr == noErr) {
  2192.         AddResource(scriptData.dataHandle, 'scpt',
  2193.             resourceNumber, rezName);
  2194.         WriteResource(resHandle);
  2195.         result = TCL_OK;
  2196.         goto rezEvalCleanUp;
  2197.         } else {
  2198.         /*
  2199.          * This means there was some other error, for now
  2200.          * I just bag out */
  2201.         sprintf(idStr, "%d", myErr);
  2202.         Tcl_AppendResult(interp, "Error #", idStr,
  2203.             " opening scpt resource named ", resourceName,
  2204.             " in file ", fileName,(char *) NULL);
  2205.         result = TCL_ERROR;
  2206.         goto rezEvalCleanUp;
  2207.         }
  2208.     } 
  2209.     }
  2210.     
  2211.     /* 
  2212.      * We get to here if the resource exists 
  2213.      * we just copy into it... 
  2214.      */
  2215.      
  2216.     scriptSize = GetHandleSize(scriptData.dataHandle);
  2217.     SetHandleSize(resHandle, scriptSize);
  2218.     HLock(scriptData.dataHandle);
  2219.     HLock(resHandle);
  2220.     BlockMove(*scriptData.dataHandle, *resHandle,scriptSize);
  2221.     HUnlock(scriptData.dataHandle);
  2222.     HUnlock(resHandle);
  2223.     ChangedResource(resHandle);
  2224.     WriteResource(resHandle);
  2225.     result = TCL_OK;
  2226.     goto rezEvalCleanUp;
  2227.             
  2228.     rezEvalError:
  2229.     sprintf(idStr, "ID=%d", resourceNumber);
  2230.     Tcl_AppendResult(interp, "The resource \"",
  2231.         (resourceName != NULL ? resourceName : idStr),
  2232.         "\" could not be loaded from ",
  2233.         (fileName != NULL ? fileName : "application"),
  2234.         ".", NULL);
  2235.  
  2236.     rezEvalCleanUp:
  2237.     if (fileRef != -1) {
  2238.     CloseResFile(fileRef);
  2239.     }
  2240.  
  2241.     UseResFile(saveRef);
  2242.     
  2243.     return result;
  2244. }
  2245.  
  2246. /*----------------------------------------------------------------------
  2247.  *
  2248.  * tclOSALoad --
  2249.  *
  2250.  *    This loads a script resource from the file named in fileName.
  2251.  *    Most of this routine is caged from the Tcl Source, from the
  2252.  *    Tcl_MacSourceCmd routine.  This is good, since it ensures this
  2253.  *    follows the same convention for looking up files as Tcl.
  2254.  *
  2255.  * Returns
  2256.  *    A standard Tcl result.
  2257.  *
  2258.  * Side Effects:
  2259.  *    A new script element is created from the data in the file.
  2260.  *    The script ID is passed out in the variable resultID.
  2261.  *
  2262.  *----------------------------------------------------------------------
  2263.  */
  2264.  
  2265. int
  2266. tclOSALoad(
  2267.     Tcl_Interp *interp,
  2268.     tclOSAComponent *theComponent,
  2269.     char *resourceName,
  2270.     int resourceNumber, 
  2271.     char *fileName,
  2272.     OSAID *resultID)
  2273. {
  2274.     Handle sourceData;
  2275.     Str255 rezName;
  2276.     int result = TCL_OK;
  2277.     short saveRef, fileRef = -1;
  2278.     char idStr[64];
  2279.     FSSpec fileSpec;
  2280.     Tcl_DString buffer;
  2281.     char *nativeName;
  2282.  
  2283.     saveRef = CurResFile();
  2284.     
  2285.     if (fileName != NULL) {
  2286.     OSErr err;
  2287.         
  2288.     Tcl_DStringInit(&buffer);    
  2289.     nativeName = Tcl_TranslateFileName(interp, fileName, &buffer);
  2290.     if (nativeName == NULL) {
  2291.         return TCL_ERROR;
  2292.     }
  2293.     err = FSpLocationFromPath(strlen(nativeName), nativeName, &fileSpec);
  2294.     Tcl_DStringFree(&buffer);
  2295.     if (err != noErr) {
  2296.         Tcl_AppendResult(interp, "Error finding the file: \"", 
  2297.             fileName, "\".", NULL);
  2298.         return TCL_ERROR;
  2299.     }
  2300.             
  2301.     fileRef = FSpOpenResFileCompat(&fileSpec, fsRdPerm);
  2302.     if (fileRef == -1) {
  2303.         Tcl_AppendResult(interp, "Error reading the file: \"", 
  2304.             fileName, "\".", NULL);
  2305.         return TCL_ERROR;
  2306.     }
  2307.     UseResFile(fileRef);
  2308.     } else {
  2309.     /*
  2310.      * The default behavior will search through all open resource files.
  2311.      * This may not be the behavior you desire.  If you want the behavior
  2312.      * of this call to *only* search the application resource fork, you
  2313.      * must call UseResFile at this point to set it to the application
  2314.      * file.  This means you must have already obtained the application's 
  2315.      * fileRef when the application started up.
  2316.      */
  2317.     }
  2318.     
  2319.     /*
  2320.      * Load the resource by name or ID
  2321.      */
  2322.     if (resourceName != NULL) {
  2323.     strcpy((char *) rezName + 1, resourceName);
  2324.     rezName[0] = strlen(resourceName);
  2325.     sourceData = GetNamedResource('scpt', rezName);
  2326.     } else {
  2327.     sourceData = GetResource('scpt', (short) resourceNumber);
  2328.     }
  2329.     
  2330.     if (sourceData == NULL) {
  2331.     result = TCL_ERROR;
  2332.     } else {
  2333.     AEDesc scriptDesc;
  2334.     OSAError osaErr;
  2335.         
  2336.     scriptDesc.descriptorType = typeOSAGenericStorage;
  2337.     scriptDesc.dataHandle = sourceData;
  2338.         
  2339.     osaErr = OSALoad(theComponent->theComponent, &scriptDesc,
  2340.         kOSAModeNull, resultID);
  2341.         
  2342.     ReleaseResource(sourceData);
  2343.         
  2344.     if (osaErr != noErr) {
  2345.         result = TCL_ERROR;
  2346.         goto rezEvalError;
  2347.     }
  2348.             
  2349.     goto rezEvalCleanUp;
  2350.     }
  2351.     
  2352.     rezEvalError:
  2353.     sprintf(idStr, "ID=%d", resourceNumber);
  2354.     Tcl_AppendResult(interp, "The resource \"",
  2355.         (resourceName != NULL ? resourceName : idStr),
  2356.         "\" could not be loaded from ",
  2357.         (fileName != NULL ? fileName : "application"),
  2358.         ".", NULL);
  2359.  
  2360.     rezEvalCleanUp:
  2361.     if (fileRef != -1) {
  2362.     CloseResFile(fileRef);
  2363.     }
  2364.  
  2365.     UseResFile(saveRef);
  2366.     
  2367.     return result;
  2368. }
  2369.  
  2370. /*
  2371.  *----------------------------------------------------------------------
  2372.  *
  2373.  * tclOSAGetScriptID  --
  2374.  *
  2375.  *    This returns the context ID, gibven the component name.
  2376.  *
  2377.  * Results:
  2378.  *    A standard Tcl result
  2379.  *
  2380.  * Side effects:
  2381.  *    Passes out the script ID in the variable scriptID.
  2382.  *
  2383.  *----------------------------------------------------------------------
  2384.  */
  2385.  
  2386. static int 
  2387. tclOSAGetScriptID(
  2388.     tclOSAComponent *theComponent,
  2389.     char *scriptName,
  2390.     OSAID *scriptID) 
  2391. {
  2392.     tclOSAScript *theScript;
  2393.     
  2394.     theScript = tclOSAGetScript(theComponent, scriptName);
  2395.     if (theScript == NULL) {
  2396.     return TCL_ERROR;
  2397.     }
  2398.     
  2399.     *scriptID = theScript->scriptID;
  2400.     return TCL_OK;
  2401. }
  2402.  
  2403. /*
  2404.  *----------------------------------------------------------------------
  2405.  *
  2406.  * tclOSAAddScript  --
  2407.  *
  2408.  *    This adds a script to theComponent's script table, with the
  2409.  *    given name & ID.
  2410.  *
  2411.  * Results:
  2412.  *    A standard Tcl result
  2413.  *
  2414.  * Side effects:
  2415.  *    Adds an element to the component's script table.
  2416.  *
  2417.  *----------------------------------------------------------------------
  2418.  */
  2419.  
  2420. static int 
  2421. tclOSAAddScript(
  2422.     tclOSAComponent *theComponent,
  2423.     char *scriptName,
  2424.     long modeFlags,
  2425.     OSAID scriptID) 
  2426. {
  2427.     Tcl_HashEntry *hashEntry;
  2428.     int newPtr;
  2429.     static int scriptIndex = 0;
  2430.     tclOSAScript *theScript;
  2431.     
  2432.     if (*scriptName == '\0') {
  2433.     sprintf(scriptName, "OSAScript%d", scriptIndex++);
  2434.     }
  2435.     
  2436.     hashEntry = Tcl_CreateHashEntry(&theComponent->scriptTable,
  2437.         scriptName, &newPtr);
  2438.     if (newPtr == 0) {
  2439.     theScript = (tclOSAScript *) Tcl_GetHashValue(hashEntry);
  2440.     OSADispose(theComponent->theComponent, theScript->scriptID);
  2441.     } else {        
  2442.     theScript = (tclOSAScript *) ckalloc(sizeof(tclOSAScript));
  2443.     if (theScript == NULL) {
  2444.         return TCL_ERROR;
  2445.     }
  2446.     }
  2447.         
  2448.     theScript->scriptID = scriptID;
  2449.     theScript->languageID = theComponent->languageID;
  2450.     theScript->modeFlags = modeFlags;
  2451.     
  2452.     Tcl_SetHashValue(hashEntry,(ClientData) theScript);
  2453.  
  2454.     return TCL_OK;
  2455. }
  2456.  
  2457. /*
  2458.  *----------------------------------------------------------------------
  2459.  *
  2460.  * tclOSAGetScriptID  --
  2461.  *
  2462.  *    This returns the script structure, given the component and script name.
  2463.  *
  2464.  * Results:
  2465.  *    A pointer to the script structure.
  2466.  *
  2467.  * Side effects:
  2468.  *    None
  2469.  *
  2470.  *----------------------------------------------------------------------
  2471.  */
  2472.  
  2473. static tclOSAScript *
  2474. tclOSAGetScript(
  2475.     tclOSAComponent *theComponent,
  2476.     char *scriptName)
  2477. {
  2478.     Tcl_HashEntry *hashEntry;
  2479.     
  2480.     hashEntry = Tcl_FindHashEntry(&theComponent->scriptTable, scriptName);
  2481.     if (hashEntry == NULL) {
  2482.     return NULL;
  2483.     }
  2484.     
  2485.     return (tclOSAScript *) Tcl_GetHashValue(hashEntry);
  2486. }
  2487.  
  2488. /*
  2489.  *----------------------------------------------------------------------
  2490.  *
  2491.  * tclOSADeleteScript  --
  2492.  *
  2493.  *    This deletes the script given by scriptName.
  2494.  *
  2495.  * Results:
  2496.  *    A standard Tcl result
  2497.  *
  2498.  * Side effects:
  2499.  *    Deletes the script from the script table, and frees up the
  2500.  *    resources associated with it.  If there is an error, then
  2501.  *    space for the error message is malloc'ed, and passed out in
  2502.  *    the variable errMsg.
  2503.  *
  2504.  *----------------------------------------------------------------------
  2505.  */
  2506.  
  2507. static int
  2508. tclOSADeleteScript(
  2509.     tclOSAComponent *theComponent,
  2510.     char *scriptName,
  2511.     char *errMsg) 
  2512. {
  2513.     Tcl_HashEntry *hashEntry;
  2514.     tclOSAScript *scriptPtr;
  2515.  
  2516.     hashEntry = Tcl_FindHashEntry(&theComponent->scriptTable, scriptName);
  2517.     if (hashEntry == NULL) {
  2518.     errMsg = ckalloc(17);
  2519.     strcpy(errMsg,"Script not found");
  2520.     return TCL_ERROR;
  2521.     }
  2522.     
  2523.     scriptPtr = (tclOSAScript *) Tcl_GetHashValue(hashEntry);
  2524.     OSADispose(theComponent->theComponent, scriptPtr->scriptID);
  2525.     ckfree((char *) scriptPtr);
  2526.     Tcl_DeleteHashEntry(hashEntry);
  2527.     return TCL_OK;
  2528. }
  2529.  
  2530. /*
  2531.  *----------------------------------------------------------------------
  2532.  *
  2533.  * TclOSAActiveProc --
  2534.  *
  2535.  *    This is passed to each component.  It is run periodically
  2536.  *    during script compilation and script execution.  It in turn
  2537.  *    calls Tcl_DoOneEvent to process the event queue.  We also call
  2538.  *    the default Active proc which will let the user cancel the script
  2539.  *    by hitting Command-.
  2540.  * 
  2541.  * Results:
  2542.  *    A standard MacOS system error
  2543.  *
  2544.  * Side effects:
  2545.  *    Any Tcl code may run while calling Tcl_DoOneEvent.
  2546.  *
  2547.  *----------------------------------------------------------------------
  2548.  */
  2549.  
  2550. static pascal OSErr 
  2551. TclOSAActiveProc(
  2552.     long refCon)
  2553. {
  2554.     tclOSAComponent *theComponent = (tclOSAComponent *) refCon;
  2555.     
  2556.     Tcl_DoOneEvent(TCL_DONT_WAIT);
  2557.     CallOSAActiveProc(theComponent->defActiveProc, theComponent->defRefCon);
  2558.     
  2559.     return noErr;
  2560. }
  2561.  
  2562. /*
  2563.  *----------------------------------------------------------------------
  2564.  *
  2565.  * ASCIICompareProc --
  2566.  *
  2567.  *    Trivial ascii compare for use with qsort.    
  2568.  *
  2569.  * Results:
  2570.  *    strcmp of the two input strings
  2571.  *
  2572.  * Side effects:
  2573.  *    None
  2574.  *
  2575.  *----------------------------------------------------------------------
  2576.  */
  2577. static int 
  2578. ASCIICompareProc(const void *first,const void *second)
  2579. {
  2580.     int order;
  2581.     
  2582.     char *firstString = *((char **) first);
  2583.     char *secondString = *((char **) second);
  2584.  
  2585.     order = strcmp(firstString, secondString);
  2586.     
  2587.     return order;
  2588. }
  2589.  
  2590. #define REALLOC_INCR 30
  2591. /*
  2592.  *----------------------------------------------------------------------
  2593.  *
  2594.  * getSortedHashKeys --
  2595.  *
  2596.  *    returns an alphabetically sorted list of the keys of the hash
  2597.  *    theTable which match the string "pattern" in the DString
  2598.  *    theResult. pattern == NULL matches all.
  2599.  *
  2600.  * Results:
  2601.  *    None
  2602.  *
  2603.  * Side effects:
  2604.  *    ReInitializes the DString theResult, then copies the names of
  2605.  *    the matching keys into the string as list elements.
  2606.  *
  2607.  *----------------------------------------------------------------------
  2608.  */
  2609.  
  2610. static void 
  2611. getSortedHashKeys(
  2612.     Tcl_HashTable *theTable,
  2613.     char *pattern,
  2614.     Tcl_DString *theResult)
  2615. {
  2616.     Tcl_HashSearch search;
  2617.     Tcl_HashEntry *hPtr;
  2618.     Boolean compare = true;
  2619.     char *keyPtr;
  2620.     static char **resultArgv = NULL;
  2621.     static int totSize = 0;
  2622.     int totElem = 0, i;
  2623.     
  2624.     if (pattern == NULL || *pattern == '\0' || 
  2625.         (*pattern == '*' && *(pattern + 1) == '\0')) {
  2626.     compare = false;
  2627.     }
  2628.     
  2629.     for (hPtr = Tcl_FirstHashEntry(theTable,&search), totElem = 0;
  2630.      hPtr != NULL; hPtr = Tcl_NextHashEntry(&search)) {
  2631.             
  2632.     keyPtr = (char *) Tcl_GetHashKey(theTable, hPtr);
  2633.     if (!compare || Tcl_StringMatch(keyPtr, pattern)) {
  2634.         totElem++;        
  2635.         if (totElem >= totSize) {
  2636.         totSize += REALLOC_INCR;
  2637.         resultArgv = (char **) ckrealloc((char *) resultArgv,
  2638.             totSize * sizeof(char *));
  2639.         }
  2640.         resultArgv[totElem - 1] = keyPtr;
  2641.     } 
  2642.     }
  2643.         
  2644.     Tcl_DStringInit(theResult);
  2645.     if (totElem == 1) {
  2646.     Tcl_DStringAppendElement(theResult, resultArgv[0]);
  2647.     } else if (totElem > 1) {
  2648.     qsort((VOID *) resultArgv, (size_t) totElem, sizeof (char *),
  2649.         ASCIICompareProc);
  2650.  
  2651.     for (i = 0; i < totElem; i++) {
  2652.         Tcl_DStringAppendElement(theResult, resultArgv[i]);
  2653.     }
  2654.     }    
  2655. }
  2656.  
  2657. /*
  2658.  *----------------------------------------------------------------------
  2659.  *
  2660.  * prepareScriptData --
  2661.  *
  2662.  *    Massages the input data in the argv array, concating the 
  2663.  *    elements, with a " " between each, and replacing \n with \r,
  2664.  *    and \\n with "  ".  Puts the result in the the DString scrptData,
  2665.  *    and copies the result to the AEdesc scrptDesc.
  2666.  *
  2667.  * Results:
  2668.  *    Standard Tcl result
  2669.  *
  2670.  * Side effects:
  2671.  *    Creates a new Handle (with AECreateDesc) for the script data.
  2672.  *    Stores the script in scrptData, or the error message if there
  2673.  *    is an error creating the descriptor.
  2674.  *
  2675.  *----------------------------------------------------------------------
  2676.  */
  2677.  
  2678. static int
  2679. prepareScriptData(
  2680.     int argc,
  2681.     char **argv,
  2682.     Tcl_DString *scrptData,
  2683.     AEDesc *scrptDesc) 
  2684. {
  2685.     char * ptr;
  2686.     int i;
  2687.     char buffer[7];
  2688.     OSErr sysErr = noErr;
  2689.         
  2690.     Tcl_DStringInit(scrptData);
  2691.     
  2692.     for (i = 0; i < argc; i++) {
  2693.     Tcl_DStringAppend(scrptData, argv[i], -1);
  2694.     Tcl_DStringAppend(scrptData, " ", 1);
  2695.     }
  2696.  
  2697.     /*
  2698.      * First replace the \n's with \r's in the script argument
  2699.      * Also replace "\\n" with "  ".
  2700.      */
  2701.      
  2702.     for (ptr = scrptData->string; *ptr != '\0'; ptr++) {
  2703.     if (*ptr == '\n') {
  2704.         *ptr = '\r';
  2705.     } else if (*ptr == '\\') {
  2706.         if (*(ptr + 1) == '\n') {
  2707.         *ptr = ' ';
  2708.         *(ptr + 1) = ' ';
  2709.         }
  2710.     }
  2711.     }
  2712.      
  2713.     sysErr = AECreateDesc(typeChar, Tcl_DStringValue(scrptData),
  2714.         Tcl_DStringLength(scrptData), scrptDesc);
  2715.                         
  2716.     if (sysErr != noErr) {
  2717.     sprintf(buffer, "%6d", sysErr);
  2718.     Tcl_DStringFree(scrptData);
  2719.     Tcl_DStringAppend(scrptData, "Error #", 7);
  2720.     Tcl_DStringAppend(scrptData, buffer, -1);
  2721.     Tcl_DStringAppend(scrptData, " creating Script Data Descriptor.", 33);
  2722.     return TCL_ERROR;                    
  2723.     }
  2724.     
  2725.     return TCL_OK;
  2726. }
  2727.  
  2728. /*
  2729.  *----------------------------------------------------------------------
  2730.  *
  2731.  * tclOSAResultFromID --
  2732.  *
  2733.  *    Gets a human readable version of the result from the script ID
  2734.  *    and returns it in the result of the interpreter interp
  2735.  *
  2736.  * Results:
  2737.  *    None
  2738.  *
  2739.  * Side effects:
  2740.  *    Sets the result of interp to the human readable version of resultID.
  2741.  *  
  2742.  *
  2743.  *----------------------------------------------------------------------
  2744.  */
  2745.  
  2746. void 
  2747. tclOSAResultFromID(
  2748.     Tcl_Interp *interp,
  2749.     ComponentInstance theComponent,
  2750.     OSAID resultID )
  2751. {
  2752.     OSErr myErr = noErr;
  2753.     AEDesc resultDesc;
  2754.     Tcl_DString resultStr;
  2755.     
  2756.     Tcl_DStringInit(&resultStr);
  2757.     
  2758.     myErr = OSADisplay(theComponent, resultID, typeChar,
  2759.         kOSAModeNull, &resultDesc);
  2760.     Tcl_DStringAppend(&resultStr, (char *) *resultDesc.dataHandle,
  2761.         GetHandleSize(resultDesc.dataHandle));
  2762.     Tcl_DStringResult(interp,&resultStr);
  2763. }
  2764.  
  2765. /*
  2766.  *----------------------------------------------------------------------
  2767.  *
  2768.  * tclOSAASError --
  2769.  *
  2770.  *    Gets the error message from the AppleScript component, and adds
  2771.  *    it to interp's result. If the script data is known, will point
  2772.  *    out the offending bit of code.  This MUST BE A NULL TERMINATED
  2773.  *    C-STRING, not a typeChar.
  2774.  *
  2775.  * Results:
  2776.  *    None
  2777.  *
  2778.  * Side effects:
  2779.  *    Sets the result of interp to error, plus the relevant portion
  2780.  *    of the script.
  2781.  *
  2782.  *----------------------------------------------------------------------
  2783.  */
  2784.  
  2785. void 
  2786. tclOSAASError(
  2787.     Tcl_Interp * interp,
  2788.     ComponentInstance theComponent,
  2789.     char *scriptData )
  2790. {
  2791.     OSErr myErr = noErr;
  2792.     AEDesc errResult,errLimits;
  2793.     Tcl_DString errStr;
  2794.     DescType returnType;
  2795.     Size returnSize;
  2796.     short srcStart,srcEnd;
  2797.     char buffer[16];
  2798.     
  2799.     Tcl_DStringInit(&errStr);
  2800.     Tcl_DStringAppend(&errStr, "An AppleScript error was encountered.\n", -1); 
  2801.     
  2802.     OSAScriptError(theComponent, kOSAErrorNumber,
  2803.         typeShortInteger, &errResult);
  2804.     
  2805.     sprintf(buffer, "Error #%-6.6d\n", (short int) **errResult.dataHandle);
  2806.  
  2807.     AEDisposeDesc(&errResult);
  2808.     
  2809.     Tcl_DStringAppend(&errStr,buffer, 15);
  2810.     
  2811.     OSAScriptError(theComponent, kOSAErrorMessage, typeChar, &errResult);
  2812.     Tcl_DStringAppend(&errStr, (char *) *errResult.dataHandle,
  2813.         GetHandleSize(errResult.dataHandle));
  2814.     AEDisposeDesc(&errResult);
  2815.     
  2816.     if (scriptData != NULL) {
  2817.     int lowerB, upperB;
  2818.         
  2819.     myErr = OSAScriptError(theComponent, kOSAErrorRange,
  2820.         typeOSAErrorRange, &errResult);
  2821.         
  2822.     myErr = AECoerceDesc(&errResult, typeAERecord, &errLimits);
  2823.     myErr = AEGetKeyPtr(&errLimits, keyOSASourceStart,
  2824.         typeShortInteger, &returnType, &srcStart,
  2825.         sizeof(short int), &returnSize);
  2826.     myErr = AEGetKeyPtr(&errLimits, keyOSASourceEnd, typeShortInteger,
  2827.         &returnType, &srcEnd, sizeof(short int), &returnSize);
  2828.     AEDisposeDesc(&errResult);
  2829.     AEDisposeDesc(&errLimits);
  2830.  
  2831.     Tcl_DStringAppend(&errStr, "\nThe offending bit of code was:\n\t", -1);
  2832.     /*
  2833.      * Get the full line on which the error occured:
  2834.      */
  2835.     for (lowerB = srcStart; lowerB > 0; lowerB--) {
  2836.         if (*(scriptData + lowerB ) == '\r') {
  2837.         lowerB++;
  2838.         break;
  2839.         }
  2840.     }
  2841.         
  2842.     for (upperB = srcEnd; *(scriptData + upperB) != '\0'; upperB++) {
  2843.         if (*(scriptData + upperB) == '\r') {
  2844.         break;
  2845.         }
  2846.     }
  2847.  
  2848.     Tcl_DStringAppend(&errStr, scriptData+lowerB, srcStart - lowerB);
  2849.     Tcl_DStringAppend(&errStr, "_", 1);
  2850.     Tcl_DStringAppend(&errStr, scriptData+srcStart, upperB - srcStart);
  2851.     }
  2852.     
  2853.     Tcl_DStringResult(interp,&errStr);
  2854. }
  2855.  
  2856. /*
  2857.  *----------------------------------------------------------------------
  2858.  *
  2859.  * GetRawDataFromDescriptor --
  2860.  *
  2861.  *    Get the data from a descriptor.
  2862.  *
  2863.  * Results:
  2864.  *    None
  2865.  *
  2866.  * Side effects:
  2867.  *    None.
  2868.  *
  2869.  *----------------------------------------------------------------------
  2870.  */
  2871.  
  2872. static void
  2873. GetRawDataFromDescriptor(
  2874.     AEDesc *theDesc,
  2875.     Ptr destPtr,
  2876.     Size destMaxSize,
  2877.     Size *actSize)
  2878.   {
  2879.       Size copySize;
  2880.  
  2881.       if (theDesc->dataHandle) {
  2882.       HLock((Handle)theDesc->dataHandle);
  2883.       *actSize = GetHandleSize((Handle)theDesc->dataHandle);
  2884.       copySize = *actSize < destMaxSize ? *actSize : destMaxSize;
  2885.       BlockMove(*theDesc->dataHandle, destPtr, copySize);
  2886.       HUnlock((Handle)theDesc->dataHandle);
  2887.       } else {
  2888.       *actSize = 0;
  2889.       }
  2890.       
  2891.   }
  2892.  
  2893. /*
  2894.  *----------------------------------------------------------------------
  2895.  *
  2896.  * GetRawDataFromDescriptor --
  2897.  *
  2898.  *    Get the data from a descriptor.  Assume it's a C string.
  2899.  *
  2900.  * Results:
  2901.  *    None
  2902.  *
  2903.  * Side effects:
  2904.  *    None.
  2905.  *
  2906.  *----------------------------------------------------------------------
  2907.  */
  2908.  
  2909. static OSErr
  2910. GetCStringFromDescriptor(
  2911.     AEDesc *sourceDesc,
  2912.     char *resultStr,
  2913.     Size resultMaxSize,
  2914.     Size *resultSize)
  2915. {
  2916.     OSErr err;
  2917.     AEDesc resultDesc;
  2918.  
  2919.     resultDesc.dataHandle = nil;
  2920.                 
  2921.     err = AECoerceDesc(sourceDesc, typeChar, &resultDesc);
  2922.         
  2923.     if (!err) {
  2924.     GetRawDataFromDescriptor(&resultDesc, (Ptr) resultStr,
  2925.         resultMaxSize - 1, resultSize);
  2926.     resultStr[*resultSize] = 0;
  2927.     } else {
  2928.     err = errAECoercionFail;
  2929.     }
  2930.             
  2931.     if (resultDesc.dataHandle) {
  2932.     AEDisposeDesc(&resultDesc);
  2933.     }
  2934.     
  2935.     return err;
  2936. }
  2937.